rand_r是否真正的线程安全?

8

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;
    }
3个回答

22
你可以将线程安全性分为三个级别,这里我将其编号以便参考。
1)完全不安全。从多个线程同时调用该函数是不安全的。例如,strtok
2)对系统而言是线程安全的。如果不同的调用操作不同的数据,则可以从多个线程同时调用该函数,例如rand_rmemcpy
3)对数据而言是线程安全的。即使在同一数据上操作,也可以从多个线程同时调用该函数。例如pthread_mutex_lockrand_r处于第二级别,在C语言中(特别是在POSIX规范中)的约定是将其称为“线程安全”。
在其他一些语言中,如Java,惯例是将第三级别称为“线程安全”,其他一切则为“不安全”。因此,java.util.Vector 是“线程安全”的,而 java.util.ArrayList 则为“不安全”。当然,java.util.ArrayList 的所有方法都处于第二级别。因此,从Java过来的程序员可能会自然地将rand_rmemcpy称为“不安全”。
在C语言中,情况不同,这可能是因为内部同步数据结构相对较少。在C语言环境中,你可能会问“文件句柄是否线程安全?”,这时指的是第三级别,但当问到“此函数是否线程安全?”时,通常是指第二级别。

15

rand_r是线程安全的,因为该函数完全是纯函数。除了参数以外,它不读取或修改任何状态,因此可以同时安全地调用。

这与大多数rand函数不同,后者将状态(种子)保存在全局变量中。

假设两个线程将在相同的时间使用相同的变量种子调用rand_r。

我假设你的意思是这样:

int globalSeed;

//thread 1
rand_r(&globalSeed);

//thread 2
rand_r(&globalSeed);

这并不意味着该函数不是线程安全的,只是因为您提供了一个输出参数,可能会被另一个线程访问/修改,从而导致您以非线程安全的方式使用它。

这就像将函数结果写入可能被另一个线程访问/修改的全局变量一样。这并不意味着函数不是线程安全的,而是您的代码不是线程安全的。


0

因为它修改了种子,而种子被传递进来。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接