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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3973665/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 14:10:24  来源:igfitidea点击:

How do I use rand_r and how do I use it in a thread safe way?

c++randomconcurrencythread-safety

提问by derrdji

I am trying to learn how to use rand_r, and after reading this questionI am still a little confused, can someone please take a look and point out what I'm missing? To my understanding, rand_r takes a pointer to some value (or a piece of memory with some initial value) and use it to generate new numbers every time it is called. Each thread that calls rand_r should supply it with a unique pointer (or piece of memory) to get "actual random" numbers between different threads. That's why this:

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

int globalSeed;

//thread 1
rand_r(&globalSeed);

//thread 2
rand_r(&globalSeed);

is the wrong way of using it. If I have

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

int seed1,seed2;

//thread 1
rand_r(&seed1);

//thread 2
rand_r(&seed2);

this would be the right way to generate "true random" numbers between threads?

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



EDIT: additional questions after reading answers to the above part:

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

  1. 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?
  2. Is it right or normal if the memory for the seed is dynamically allocated?
  1. 如果在线程 1 中我需要 1 到 n 之间的随机数,我应该这样做 (rand_r(&seed1) % (n-1)) + 1吗?或者有其他常见的方法来做到这一点?
  2. 如果种子的内存是动态分配的,这是正确的还是正常的?

采纳答案by paxdiablo

That's correct. What you're doing in the first case is bypassing the thread-safety nature of rand_r. With many non-thread-safe functions, persistent state is stored between calls to that function (such as the random seed here).

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

With the thread-safe variant, you actually provide a thread-specific piece of data (seed1and seed2) to ensure the state is not shared between threads.

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

Keep in mind that this doesn't make the numbers truly random, it just makes the sequences independent of each other. If you start them with the same seed, you'll probably get the same sequence in both threads.

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

By way of example, let's say you get a random sequence 2, 3, 5, 7, 11, 13, 17 given an initial seed of 0. With a shared seed, alternating calls to rand_rfrom two different threads would cause this:

举个例子,假设你得到一个随机序列 2, 3, 5, 7, 11, 13, 17 给定初始种子 0。 使用共享种子,rand_r来自两个不同线程的交替调用会导致这种情况:

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

and that's the bestcase - you may actually find that the shared state gets corrupted since the updates on it may not be atomic.

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

With non-shared state (with aand brepresenting the two different sources of the random numbers):

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

thread 1                thread 2
           <---  2a
                 2b --->
           <---  3a
                 3b --->
           <---  5a
                 5b --->
                 ::

Some thread-safe calls require you to provide the thread-specific state like this, others can create thread-specific data under the covers (using a thread ID or similar information) so that you never need to worry about it, and you can use exactly the same source code in threaded and non-threaded environments. I prefer the latter myself, simply because it makes my life easier.

一些线程安全调用要求您提供这样的线程特定状态,其他人可以在幕后创建线程特定数据(使用线程 ID 或类似信息),这样您就无需担心它,您可以使用线程和非线程环境中的源代码完全相同。我自己更喜欢后者,只是因为它让我的生活更轻松。



Additional stuff for edited question:

编辑问题的其他内容:

> 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?

> 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?

Assuming you want a value between 1and ninclusive, use (rand_r(&seed1) % n) + 1. The first bit gives you a value from 0to n-1inclusive, then you add 1 to get the desired range.

假设您想要一个介于1n包含的值,请使用(rand_r(&seed1) % n) + 1. 第一位为您提供一个值 from 0to n-1inclusive,然后加 1 以获得所需的范围。

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

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

The seed has to be persistent as long as you're using it. You could dynamically allocate it in the thread but you could also declare it in the thread's top-level function. In both those cases, you'll need to communicate the address down to the lower levels somehow (unless your thread is just that one function which is unlikely).

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

You could either pass it down through the function calls or set up a global array somehow where the lower levels can discover the correct seed address.

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

Alternatively, since you need a global array anyway, you can have a global array of seeds rather than seed addresses, which the lower levels could use to discover their seed.

或者,由于无论如何您都需要一个全局数组,您可以拥有一个全局种子数组而不是种子地址,较低级别可以使用它来发现他们的种子。

You would probably (in both cases of using the global array) have a keyed structure containing the thread ID as a key and the seed to use. You would then have to write your ownrand()routine which located the correct seed and called rand_r()with that.

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

Thisis why I prefer library routines which do this under the covers with thread-specific data.

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