strtok函数和multithreading

我读了很多关于strtok(char * s1,char * s2)及其实现的内容。 但是,我仍然无法理解在multithreading程序中使用它是一个危险的function。 有人可以给我一个multithreading程序的例子并解释那里的问题吗? 请注意,我正在寻找一个示例,告诉我问题出在哪里。

ps:strtok(char * s1,char * s2)是C标准库的一部分。

这是一个具体的例子:

首先假设您的程序是multithreading的,并且在一个执行线程中,运行以下代码:

 char str1[] = "split.me.up"; // call this line A char *word1 = strtok(str1, "."); // returns "split", sets str1[5] = '\0' // ... // call this line B char *word2 = strtok(NULL, "."); // we hope to get back "me" 

在另一个线程中,以下代码运行:

 char str2[] = "multi;token;string"; // call this line C char *token1 = strtok(str2, ";"); // returns "multi", sets str2[5] = '\0' // ... // call this line D char *token2 = strtok(NULL, ";"); // we hope to get back "token" 

关键是,我们真的不知道word2token2会是什么:

如果命令按顺序(A),(B),(C),(D)运行,那么我们将得到我们想要的。

但是,如果命令按顺序(A),(C),(B),(D)运行,则命令(B)将搜索a . "token;string"分隔符! 这是因为命令(B)的NULL第一个参数告诉strtok继续搜索它传递的最后一个非NULL搜索字符串,并且因为命令(C)已经运行, strtok将使用str2

然后命令(B)将返回token;string ,同时将搜索的新起始字符设置为str2末尾的NUL终止符。 然后命令(D)将认为它正在搜索空字符串,因为它将在str2NUL终结符处开始搜索,因此也将返回NULL

即使您将命令(A)和(B)彼此相邻放置,并且命令(C)和(D)彼此相邻,也不能保证(B)将在(A)之后立即执行(C)或(D)等

如果您创建某种互斥或备用防护以保护strtok函数的使用,并且只从已获得锁定所述互斥锁的线程调用strtok ,则strtok可以安全使用。 但是,正如其他人所说的那样,使用线程安全的strtok_r可能更好。

编辑:还有一个问题,没有其他人提到过,即strtok修改并可能使用全局(或静态,无论)变量,并且可能以非线程安全的方式这样做,所以即使你不这样做依赖于重复调用strtok从同一个字符串中获取连续的“标记”,在没有保护等的multithreading环境中使用它可能不安全。

在第一次调用strtok时,您提供了字符串和分隔符。 在后续调用中,第一个参数为NULL,您只需提供分隔符。 strtok会记住你传入的字符串。

在multithreading环境中,这很危险,因为许multithreading可能使用不同的字符串调用strtok。 它只会记住最后一个并返回错误的结果。

用简单的术语来解释,每当他们将它命名为THREAD安全时,他们的字面意思是,它不仅仅是你的线程,其他线程也可以修改它! 这就像一个蛋糕同时与5位朋友分享。 结果是不可预测的谁吃蛋糕,或谁改变它!

每次调用strtok()函数都会返回一个以NULL结尾的字符串的引用,并在解析过程中使用静态缓冲区。 对函数的任何后续调用都将仅引用该缓冲区,并且它会被更改。 它独立于谁调用它,这就是它不是线程安全的原因。

另一方面strtok_r()使用另一个名为saveptr的第三个参数(我们需要指定它),它可能用于保存后续调用的引用。 因此不再是系统特定的,而是在开发人员控制中。

一个例子 :(来自Steven robbins的书,unix系统编程)

不正确地使用strtok来确定每行的平均单词数。

 #include  #define LINE_DELIMITERS "\n" #define WORD_DELIMITERS " " static int wordcount(char *s) { int count = 1; if (strtok(s, WORD_DELIMITERS) == NULL) return 0; while (strtok(NULL, WORD_DELIMITERS) != NULL) count++; return count; } double wordaverage(char *s) { /* return average size of words in s */ int linecount = 1; char *nextline; int words; nextline = strtok(s, LINE_DELIMITERS); if (nextline == NULL) return 0.0; words = wordcount(nextline); while ((nextline = strtok(NULL, LINE_DELIMITERS)) != NULL) { words += wordcount(nextline); linecount++; } return (double)words/linecount; } 

wordaverage函数通过使用strtok查找下一行来确定每行的平均单词数。 然后该函数调用wordcount来计算此行上的单词数。 不幸的是, wordcount也使用了strtok ,这次要解析就行了。 如果另一个函数没有调用strtok那么每个函数本身都是正确的。 wordaverage函数对第一行正常工作,但是当wordaverage调用strtok来解析第二行时, strtok保存的内部状态信息已被wordcount重置。