如何使用rand_r和如何使用它在线程安全的方式? [英] How do I use rand_r and how do I use it in a thread safe way?

查看:4507
本文介绍了如何使用rand_r和如何使用它在线程安全的方式?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  int globalSeed; 

//线程1
rand_r(& globalSeed);

//线程2
rand_r(& globalSeed);

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

  int seed1,seed2; 

//线程1
rand_r(& seed1);

//线程2
rand_r(& seed2);

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






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


  1. 如果在线程1中我需要一个随机的
    数字在1到n之间,我应该
    (rand_r(& seed1)%(n-1)) + 1

$ b如果动态分配种子的内存是正确的还是正常的? $ b

解决方案

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



实际上提供了一个线程特定的数据( seed1 seed2 ),以确保线程之间不共享状态。



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



例如,让我们得到一个随机序列2,使用共享的种子,从两个不同的线程交替调用 rand_r 会导致:

 线程1线程2 
<--- 2
3 --->
< --- 5
7 --->
< --- 11
13 --->
< --- 17



对于非共享状态(),您可能会发现共享状态已损坏,因为它的更新可能不是原子。代表随机数的两个不同来源的

$ <$ p $ c>和 b ):

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

一些线程安全调用需要你提供线程特定状态这,其他人可以创建线程特定的数据(使用线程ID或类似信息),以便您永远不需要担心它,您可以在线程和非线程环境中使用完全相同的源代码。






编辑过的问题的其他内容:


>如果在线程1中,我需要一个在1到n之间的随机数,应该是'(rand_r(& seed1)%(n-1))+ 1',还是有其他常见的方法? c $ c>


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


>如果动态分配种子的内存是正确的或正常的?


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



或者通过函数调用传递它,或者以某种方式设置一个全局数组,其中低层可以发现正确的种子地址。



或者,因为你需要一个全局数组,你可以有一个全局的种子数组,而不是种子地址,较低的水平可以用来发现它们的种子。



你可能(在使用全局数组)具有包含线程ID作为键和要使用的种子的键控结构。然后你必须写你自己的 rand()例程,它找到正确的种子并调用 rand_r()

是为什么我更喜欢使用线程特定数据覆盖的库例程。


I am trying to learn how to use rand_r, and after reading this question I 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:

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?

解决方案

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).

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

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_r from two different threads would cause this:

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

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

With non-shared state (with a and b representing the two different sources of the random numbers):

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.


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?

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

> 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 own rand() routine which located the correct seed and called rand_r() with that.

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

这篇关于如何使用rand_r和如何使用它在线程安全的方式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆