线程安全及其strtok与strtok_r的线程安全问题

线程安全
  因为同一进程中的线程共享全局,静态及堆区数据,如果两个以上的线程同时操作同一个共享的变量,将会导致执行的结果与预期不同,所以采用了加锁机制, 当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用,不会出现数据不一致或者数据污染,即称为 线程安全

 

线程不安全

是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。

(系统提供的系统调用函数或者是库函数本身实现时用到了共享的变量

 

可重入函数

可重入函数主要用于多任务环境中,一个可重入的函数简单来说就是可以被中断的函数,也就是说,可以在这个函数执行的任何时刻中断它,转入OS调度下去执行另外一段代码,而返回控制时不会出现什么错误;而不可重入的函数由于使用了一些系统资源,比如全局变量区,中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是不能运行在多任务环境下的。

Ps编写可重入函数时,若使用全局变量,则应通过关中断、关信号量(即PV操作)等手段对其加以保护。若对所使用的全局变量不加以保护,则此函数就不具有可重入性,即当多个进程调用此函数时,很有可能使有关全局变量变为不可知状态。


可重入版本的函数:

1: char *strtok( char *strToken,constchar *strDelimit );        //不保证线程安全的

/* 

原理:分解字符串为一组字符串。strToken为要分解的字符串,strDelimit分隔符字符串(如果传入字符串,则传入的字符串中每个字符均为分割符)。首次调用时,将要解析的字符串地址作为第一个参数strToken传递进去,之后再次调用要把strToken设成NULL(即NULL作为第一个参数),

strtok的每个后继调用都返回下一个标记的起始(可用this指针指向理解),并在返回的标记末尾插入一个'/0'。当strtok函数到达strToken的末尾时,就返回NULL)

*/

PS

strtok没有为标记分配新的空间,而是就地对strToken进行了标记(即将分隔符设置为‘/0’,相当于将当前字符串改变了,因为字符串遇到‘/0’即结束),理解这一点是很重要的。因此,如果需要在调用该函数后访问原来的strToken,就必须传递字符串的一个拷贝。

例题:我们将字符串char buffer[]="lucy female 20,johnmale 23"中姓名性别及年龄分开,我们可以采用两次循环,先根据分隔各个成员之间的信息,再在内层循环根据“  ”分隔每位成员的姓名性别及年龄,接下来我们用代码测试。

 

由图可以看出执行的结果有点出乎我们的意料,这是为什莫呢?

 

分析:

第一次外循环中,strtok"lucy female20,"后的这个逗号,改为了'/0’这时strtok内部的this指针指向的是逗号的后一个字符'j’经过第一次的内循环,分别提取出了”lucy” “female”“20 “。提取完"20”之后,函数内部的this指针被修改指向了"20”后面的'/0’内循环结束后,开始第二次的外循环,由于函数第一个参数被设定为NULLstrtok将以this指针指向的位置作为分解起始位置。但是,此时this指针指向的是'/0’strtok对一个空串无法切分,返回NULL。外循环结束。所以,我们只得到了如图所示的第一个人的信息。

 

这个原因其实也反映了这个函数的线程不安全性,因为根据其定义,它必须使用内部静态变量来记录字符串中下一个需要解析的标记的当前位置。但是,由于指示这个位置的变量只有一个(可以理解为内部只有一个this指针,不论你当前线程调用多少次这个函数,这个函数始终只有一个指针指向下一次分隔的位置,所以在某一个strtok中改变其指向,在下一个strtok中调用就有可能会出现问题),那么,在同一个程序中出现多个解析不同字符串的strtok调用时,各自的字符串的解析就会互相干扰。可以理解为当调用strtok时,如果找到分隔符,则将分隔符改为‘/0’并将函数内部的this指针指向被修改的分隔符的下一位作为下次分隔的起始位置,如果没找到分隔符,则将this指针指向字符串末尾的‘/0’,下次再调用strtok时,则返回NULL,函数结束。

 

所以,针对这个不安全的函数我们需要找一个替代它的安全的可重入函数:strtok_r



猜你喜欢

转载自blog.csdn.net/Eunice_fan1207/article/details/80087569