我如何使用rand_r以及如何以线程安全的方式使用它?

时间:2010-10-19 23:35:12

标签: c++ random concurrency thread-safety

我正在尝试学习如何使用rand_r,在阅读this question之后我仍然有点困惑,有人可以看看并指出我缺少的东西吗?据我所知,rand_r接受一个指向某个值的指针(或一个具有一些初始值的内存),并在每次调用时使用它来生成新数字。每个调用rand_r的线程都应该为它提供一个唯一的指针(或一块内存),以获得不同线程之间的“实际随机”数字。这就是为什么:

int globalSeed;

//thread 1
rand_r(&globalSeed);

//thread 2
rand_r(&globalSeed);

是使用它的错误方法。如果我有

int seed1,seed2;

//thread 1
rand_r(&seed1);

//thread 2
rand_r(&seed2);

这是在线程之间生成“真随机”数字的正确方法吗?


编辑:阅读上述部分答案后的其他问题:

  1. 如果在主题1中我需要随机 我应该在1到n之间的数字 (rand_r(&seed1) % (n-1)) + 1?或者还有其他常见的方法吗?
  2. 如果动态分配种子的内存是正确还是正常?

1 个答案:

答案 0 :(得分:15)

这是对的。你在第一种情况下所做的是绕过rand_r的线程安全特性。对于许多非线程安全函数,持久状态存储在对该函数的调用之间(例如此处的随机种子)。

使用线程安全的变体,您实际上提供了一个特定于线程的数据(seed1seed2),以确保线程之间不共享状态。

请记住,这并不会使数字真正随机,它只会使序列彼此独立。如果你使用相同的种子启动它们,你可能会在两个线程中得到相同的序列。

举例来说,假设你获得了一个随机序列2,3,5,7,11,13,17,初始种子为0.使用共享种子,从两个交替调用rand_r不同的线程会导致这个:

thread 1                thread 2
           <---  2
                 3 --->
           <---  5
                 7 --->
           <--- 11
                13 --->
           <--- 17

这就是最佳情况 - 您实际上可能会发现共享状态已损坏,因为它的更新可能不是原子的。

使用非共享状态(ab代表随机数的两个不同来源):

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?

假设您想要1n 包含之间的值,请使用(rand_r(&seed1) % n) + 1。第一位为您提供从0n-1的值,然后添加1以获得所需的范围。

  

> Is it right or normal if the memory for the seed is dynamically allocated?

只要你使用它,种子必须是持久的。您可以在线程中动态分配它,但您也可以在线程的顶级函数中声明它。在这两种情况下,你需要以某种方式将地址传递给较低级别​​(除非你的线程只是一个不太可能的函数)。

您可以通过函数调用将其传递下来,也可以设置一个全局数组,以便较低级别可以发现正确的种子地址。

或者,既然你需要一个全局数组,你可以拥有一个全局数组的种子而不是种子地址,较低级别可以用来发现它们的种子。

您可能(在使用全局数组的两种情况下)都有一个键控结构,其中包含作为键的线程ID和要使用的种子。然后,您必须编写自己的 rand()例程,该例程找到正确的种子,然后使用该例程调用rand_r()

这个就是为什么我更喜欢使用特定于线程的数据执行此操作的库例程。