关于strtok 和 strtok_r的可/不可重入线程安全问题的探讨

线程安全

多个线程对同一个变量进行操作时,为避免同时操作造成数据丢失,数据混乱,引入线程安全问题。
多线程执行并不是同一时刻进行的,单核情况下,只能一个个执行,只不过多线程属于交叉执行,每个线程执行时间由CPU分配,该时间片由线程去竞争。双核情况下,最多两个同时执行,以此类推。多核系统代表有多个CPU,可同时处理多条指令。
本文章为个人对该方面的总结和见解,如有不足请指正!感谢!

首先理解可重入和不可重入的概念:

可重入:表示当主程序进行时,由外部打断并执行相应处理函数后,对主程序后继执行不产生影响。一般可重入函数不对全局变量和静态局部变量等共享资源进行操作,避免外部中断或多个任务同时执行时发生数据混乱出错。
不可重入:表示当主程序进行时,由外部打断并执行相应处理函数后,对主程序后继执行产生影响。数据一般为局部变量,用完即释放,只对中断当前时刻做处理,返回时,不改变主程序的环境变量。

以strtok与strtok_r为例

1. strtok为不可重入函数
——在对字符串分隔操作时,会对源字符串数据内存空间进行修改,即对指定的分隔符替换成 ‘\0’ ,通过静态变量记录上一次扫描数组的位置,返回当前所截取的子字符串首地址,从而下一次扫描时,可以通过该静态变量获取第一个分隔符后一段字符串,反复操作获取分隔后的子字符串。
在strtok函数中,存在多线程同时操作时的问题,每个子线程调用strtok函数时,由于执行字符串的位置指针由函数内部静态变量所决定,每个线程所调用的strtok函数内的静态变量记录的是上一次已完成分隔后的位置。即多个线程之间相互影响,导致数据有可能发生错误。
举个例子:
假设strtok函数内的静态变量为T,字符串数组为s[] = “WKJ\LOVE\YHL”,分隔符为 ‘\’
如果A、B、C线程中
1.A线程首先调用strtok函数完成分隔操作后,T指向’L’的位置。
2.B先调用但未结束、期间C线程在调用,B调用结束后,此时字符串数组为"WKJ\0LOVE\0YHL",分隔符均已改为’\0’,并将静态变量T指向’Y’,而C仍在进行,此时在C线程中的T变量也发生变化,幸运的话有可能指向‘Y’,也有可能发生混乱导致段错误。

2. strtok_r为可重入函数
——在strtok_r函数中,字符串的位置指针由调用者自行分配,作为传入传出参数(局部变量),只在当前任务下有效,不影响其他任务的执行。

下面分享一下个人对这两个函数的实现
注意:不是基于C库的,主要是为了加深可重入和不可重入的区别,代码函数部分不要看,只需知道处理数据的作用域,重在理解。

				********不可重入****************
//全局变量
char *tmp;
char s[] = "wkj/ai/yehuiling/1314";
char *saveptr = s;

char *strtok(char *str, const char *delim)
{
        static char *tmp = NULL,*ret;
        if(str != NULL)
                tmp = str;
        ret = tmp;
        while(*tmp != *delim && *tmp != 0){
                tmp++;
        }
        if(*tmp == *delim){
                *tmp = '\0';
                tmp++;
                return ret;
        }
        return NULL;
}

void *th_func(void *arg)
{
        tmp = strtok(s,"/");
        printf("%s\n",tmp);
        while((tmp = strtok(NULL,"/")) != NULL)
                printf("%s\n",tmp);
}

int main()
{
        pthread_t tid;
        pthread_create(&tid,NULL,th_func,NULL);
        tmp = strtok(s,"/");
        printf("%s\n",tmp);
        while((tmp = strtok(NULL,"/")) != NULL)
                printf("%s\n",tmp);
        pthread_join(tid,NULL);
        return 0;
}
               ***********************可重入************************
char *strtok_r(const char *delim, char **saveptr)
{
        if(*(*saveptr) == 0)
                return NULL;
        char *ret;
        ret = *saveptr;
        while((*(*saveptr) != *delim) && (*(*saveptr) != 0))
                (*saveptr)++;
        if(*(*saveptr) == *delim){
                *(*saveptr)++ = 0;
        }
        return ret;
}

void *th_func(void *arg)
{
        char *tmp;
        char s[] = "wkj/ai/yehuiling/1314";
        char *saveptr = s;
        while((tmp = strtok_r("/",&saveptr)) != NULL)
                printf("%s\n",tmp);
}

int main()
{
        char *tmp;
        char s[] = "wkj/ai/yehuiling/1314";
        char *saveptr = s;
        pthread_t tid;
        pthread_create(&tid,NULL,th_func,NULL);
        while((tmp = strtok_r("/",&saveptr)) != NULL)
                printf("%s\n",tmp);
        pthread_join(tid,NULL);
        return 0;
}

								结果对比

不可重入下的多线程输出结果(混乱):
在这里插入图片描述
在这里插入图片描述
可重入下的多线程输出结果(各线程间不影响,正常执行):
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值