没错。您在第一种情况下所做的是绕过rand_r 的线程安全特性。对于许多非线程安全函数,在调用该函数之间会存储持久状态(例如这里的随机种子)。
使用线程安全变体,您实际上提供了一个特定于线程的数据(seed1 和 seed2),以确保线程之间不共享状态。
请记住,这不会使数字真正随机化,它只会使序列彼此独立。如果您使用相同的种子启动它们,您可能会在两个线程中获得相同的序列。
举例来说,假设您得到一个随机序列 2、3、5、7、11、13、17,初始种子为 0。使用共享种子,从两个不同线程交替调用 rand_r会导致这个:
thread 1 thread 2
<--- 2
3 --->
<--- 5
7 --->
<--- 11
13 --->
<--- 17
这是 最好的 情况 - 您实际上可能会发现共享状态已损坏,因为它的更新可能不是原子的。
非共享状态(a 和 b 代表随机数的两个不同来源):
thread 1 thread 2
<--- 2a
2b --->
<--- 3a
3b --->
<--- 5a
5b --->
::
一些线程安全调用要求您像这样提供特定于线程的状态,其他调用可以在幕后创建特定于线程的数据(使用线程 ID 或类似信息),这样您就不必担心它了,并且您可以在线程和非线程环境中使用完全相同的源代码。我自己更喜欢后者,只是因为它让我的生活更轻松。
已编辑问题的其他内容:
> If in thread 1, I need a random number between 1 to n, should I do '(rand_r(&seed1) % (n-1)) + 1', or there is other common way of doing this?
假设您想要一个介于1 和n 之间的值包含,请使用(rand_r(&seed1) % n) + 1。第一位为您提供从 0 到 n-1 的值,然后加 1 以获得所需的范围。
> Is it right or normal if the memory for the seed is dynamically allocated?
只要您在使用种子,它就必须是持久的。您可以在线程中动态分配它,但也可以在线程的顶级函数中声明它。在这两种情况下,您都需要以某种方式将地址向下传递到较低级别(除非您的线程只是一个不太可能的函数)。
您可以通过函数调用将其传递下去,或者以某种方式设置一个全局数组,使较低级别可以发现正确的种子地址。
或者,既然您无论如何都需要一个全局数组,那么您可以拥有一个全局种子数组而不是种子地址,较低级别可以使用它来发现它们的种子。
您可能(在使用全局数组的两种情况下)都有一个键控结构,其中包含作为键的线程 ID 和要使用的种子。然后,您必须编写自己的自己的 rand() 例程,该例程找到正确的种子并用它调用rand_r()。
这就是为什么我更喜欢使用线程特定数据在后台执行此操作的库例程。