好吧,rand_r函数应该是一个线程安全的函数。但是,通过它的实现,我无法相信它本身不能被其他线程改变。 假设两个线程将使用相同的变量种子同时调用rand_r。 所以会发生读写比赛。 由glibc实现的代码rand_r如下所示。谁知道为什么rand_r被称为线程安全?
int
rand_r (unsigned int *seed)
{
unsigned int next = *seed;
int result;
next *= 1103515245;
next += 12345;
result = (unsigned int) (next / 65536) % 2048;
next *= 1103515245;
next += 12345;
result <<= 10;
result ^= (unsigned int) (next / 65536) % 1024;
next *= 1103515245;
next += 12345;
result <<= 10;
result ^= (unsigned int) (next / 65536) % 1024;
*seed = next;
return result;
}
答案 0 :(得分:18)
您可以考虑三个级别的线程安全性,为了便于参考,我将在此处编号。
1)根本不是线程安全的。从多个线程同时调用该函数是不安全的。例如,strtok
。
2)相对于系统的线程安全。如果不同的调用对不同的数据进行操作,则可以安全地从多个线程同时调用该函数。例如,rand_r
,memcpy
。
3)关于数据的线程安全。从多个线程同时调用该函数是安全的,甚至可以对相同的数据执行操作。例如pthread_mutex_lock
。
rand_r
处于第2级,C语境中的约定(特别是在POSIX规范中)是称为“线程安全”。
在其他一些语言中,例如Java,惯例是将级别3称为“线程安全”,将其他所有语言称为“非线程安全”。因此,例如java.util.Vector
是“线程安全的”而java.util.ArrayList
是“非线程安全的”。当然java.util.ArrayList
的所有方法都在2级。所以来自Java的程序员可能会自然地调用rand_r
和memcpy
“不是线程安全的”。
在C中,约定是不同的,可能是因为内部同步的数据结构很少开始。在C的上下文中,您可能会问“文件是否处于线程安全?”,并且正在讨论第3级,但在询问“这个函数是否是线程安全的?”时这通常意味着第2级。
答案 1 :(得分:13)
rand_r
是线程安全的,因为该函数完全是纯粹的。除了参数之外,它不会读取或修改任何状态。因此可以安全地同时调用它。
这与将状态(种子)保存在全局变量中的大多数rand
函数不同。
假设两个线程将使用相同的变量种子同时调用rand_r。
我假设你的意思是这样的
int globalSeed;
//thread 1
rand_r(&globalSeed);
//thread 2
rand_r(&globalSeed);
这并不意味着该函数不是线程安全的,这只意味着您通过提供可由另一个线程访问/修改的输出参数以非线程安全的方式使用它。
将函数结果写入可由另一个线程访问/修改的全局变量是一回事。这并不意味着该函数不是线程安全的,这意味着您的代码不是线程安全的。
答案 2 :(得分:0)
因为它会修改种子和种子传入。