没错。你在第一种情况下所做的是绕过rand_r
线程安全的特性。对于许多非线程安全函数,持久状态会在调用该函数时存储(这里是随机种子)。
对于线程安全变体,你实际上提供了一个线程特定的数据(seed1
和seed2
),以确保状态不在线程之间共享。
请记住,这并不能使数字真正随机,它只是使序列相互独立。如果你使用相同的种子开始它们,那么两个线程中的随机序列可能会相同。
举个例子,假设你使用初始种子0得到一个随机序列2、3、5、7、11、13、17。使用共享种子,在来自两个不同线程的交替调用rand_r
时,会出现这样的情况:
thread 1 thread 2
<
3
<
7
<
13
<
而这只是最好的情况 - 你可能会发现共享状态被破坏了,因为对它的更新可能不是原子的。
如果状态不是共享的(例如a和b代表两个不同的随机数源):
thread 1 thread 2
<--- 2a
2b --->
<--- 3a
3b --->
<--- 5a
5b --->
::
一些线程安全的调用需要你提供特定于线程的状态,其他一些可以在内部创建特定于线程的数据(使用线程ID或类似信息),以便你永远不必担心它,可以在多线程和非多线程环境下使用完全相同的源代码。我个人更喜欢后者,因为这样会让我的生活更轻松。
编辑后问题的附加内容:
> 如果在线程1中,我需要1到n之间的随机数,我应该这样做'(rand_r(&seed1)%(n-1))+1',还是有其他常见的方法可以实现?
假设你想要一个值在1
到n
之间的(包括边界),请使用(rand_r(&seed1) % n) + 1
。第一个取模运算符可以得到一个从0
到n-1
的值,然后加上1即可得到所需范围内的值。
> 如果对于种子来说将内存动态分配是正确或正常的吗?
只要你在使用它,种子就必须是持久的。你可以在线程中动态分配它,但也可以在线程的顶层函数中声明它。在这两种情况下,你都需要以某种方式将地址传递到较低级别(除非你的线程只是一个函数,这是不太可能的)。
你可以通过函数调用向下传递它,或者设置全局数组,在其中较低级别可以发现正确的种子地址。
或者,由于无论如何你都需要一个全局数组,你可以拥有一组种子而不是种子地址,较低级别可以使用这些种子来发现它们的种子。
在这两种情况下,你都可能会有一个包含线程ID作为键和要使用的种子的键值结构。然后,你将不得不编写自己的rand()
例程,该例程将找到正确的种子并使用它调用rand_r()
。
这就是我更喜欢使用库例程的原因,这些例程在内部使用特定于线程的数据完成这项工作。
rand_r
来避免损坏的可能性。您不想做的一件事是使用相同的种子初始化两个序列,因为这些序列将是相同的。 - paxdiablo