【问题标题】:How do I use rand_r and how do I use it in a thread safe way?如何使用 rand_r 以及如何以线程安全的方式使用它?
【发布时间】:2010-10-19 23:35:12
【问题描述】:

我正在尝试学习如何使用 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. 种子的内存是动态分配的,是正确的还是正常的?

【问题讨论】:

    标签: c++ random concurrency thread-safety


    【解决方案1】:

    没错。您在第一种情况下所做的是绕过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 或类似信息),这样您就不必担心它了,并且您可以在线程和非线程环境中使用完全相同的源代码。我自己更喜欢后者,只是因为它让我的生活更轻松。


    已编辑问题的其他内容:

    &gt; If in thread 1, I need a random number between 1 to n, should I do '(rand_r(&amp;seed1) % (n-1)) + 1', or there is other common way of doing this?

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

    &gt; Is it right or normal if the memory for the seed is dynamically allocated?

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

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

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

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

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

    【讨论】:

    • 谢谢!!所以最好的方法总是为不同的线程使用具有不同初始值的不同种子,对吧?
    • 我通常会说,是的。但是在某些情况下,您可能想要共享状态(我的回答中的第一个图表),在这种情况下,您可以通过调用rand_r 来避免损坏的可能性 same状态,但受互斥体保护。您不想做的一件事是用相同的种子初始化这两个序列,因为这两个序列将是相同的。
    • 好的,谢谢!!我正要问我是否可以共享一个并用互斥锁保护 rand_r 调用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-08
    • 1970-01-01
    • 2011-09-29
    • 2014-03-10
    • 1970-01-01
    • 1970-01-01
    • 2014-07-11
    相关资源
    最近更新 更多