多个线程对同一个变量进行操作时,为避免同时操作造成数据丢失,数据混乱,引入线程安全问题。
多线程执行并不是同一时刻进行的,单核情况下,只能一个个执行,只不过多线程属于交叉执行,每个线程执行时间由CPU分配,该时间片由线程去竞争。双核情况下,最多两个同时执行,以此类推。多核系统代表有多个CPU,可同时处理多条指令。
本文章为个人对该方面的总结和见解,如有不足请指正!感谢!
可重入:表示当主程序进行时,由外部打断并执行相应处理函数后,对主程序后继执行不产生影响。一般可重入函数不对全局变量和静态局部变量等共享资源进行操作,避免外部中断或多个任务同时执行时发生数据混乱出错。
不可重入:表示当主程序进行时,由外部打断并执行相应处理函数后,对主程序后继执行产生影响。数据一般为局部变量,用完即释放,只对中断当前时刻做处理,返回时,不改变主程序的环境变量。
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;
}
结果对比