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

der*_*dji 21 c++ random concurrency thread-safety

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

int globalSeed;

//thread 1
rand_r(&globalSeed);

//thread 2
rand_r(&globalSeed);
Run Code Online (Sandbox Code Playgroud)

是错误的使用方式.如果我有

int seed1,seed2;

//thread 1
rand_r(&seed1);

//thread 2
rand_r(&seed2);
Run Code Online (Sandbox Code Playgroud)

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


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

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

pax*_*blo 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
Run Code Online (Sandbox Code Playgroud)

这是最好的情况 - 你可能会发现共享状态被破坏,因为它的更新可能不是原子的.

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

thread 1                thread 2
           <---  2a
                 2b --->
           <---  3a
                 3b --->
           <---  5a
                 5b --->
                 ::
Run Code Online (Sandbox Code Playgroud)

一些线程安全调用要求您提供这样的特定于线程的状态,其他人可以在封面下创建特定于线程的数据(使用线程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.第一位为您提供从包含0到的值n-1,然后添加1以获得所需的范围.

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

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

您可以通过函数调用将其传递下来,或者以某种方式设置全局数组,其中较低级别可以发现正确的种子地址.

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

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

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