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)
这是在线程之间生成"真随机"数字的正确方法吗?
编辑:阅读上述部分的答案后的其他问题:
(rand_r(&seed1) % (n-1)) + 1怎么办
?或者还有其他常见的方法吗?pax*_*blo 15
那是对的.你在第一种情况下所做的是绕过线程安全性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
Run Code Online (Sandbox Code Playgroud)
这是最好的情况 - 你可能会发现共享状态被破坏,因为它的更新可能不是原子的.
使用非共享状态(使用a并b表示随机数的两个不同来源):
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?
假设你想之间的值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()用它调用.
这就是为什么我更喜欢使用特定于线程的数据执行此操作的库例程.