【问题标题】:Correct way to use rand_r for multithreaded programs in C/C++在 C/C++ 中将 rand_r 用于多线程程序的正确方法
【发布时间】:2015-06-10 12:36:14
【问题描述】:

所以,我需要一些关于 C++ 中多线程的帮助。我想让我的多个线程使用低于 800 的随机数调用函数 usleep。但是,据我了解,rand_r 必须在每个线程中使用不同的整数作为种子。

My confusion 源于这样一个事实:如果我想为每个线程的 rand_r 使用不同的整数,那么我该怎么做呢?如果我不能创建随机整数,如何为每个线程使用不同的(即随机)整数?

static unsigned int consumerseed = 1;
static unsigned int producerseed = 54321;

//producer thread:
void *producer(void *param) {
    buffer_item rand;
    while (1) {
        //sleep for a random period of time:
        int producersleeptime = rand_r(&producerseed)%500+100;
        usleep(producersleeptime);
        //produce an item:
        //wait on both semaphores
        //attempt to insert into buffer 
        //signal both semaphores
    }
}

//consumer thread:
void *consumer(void *param) {
    buffer_item rand;
    while (1) {
        //sleep for a random period of time:
        int consumersleeptime = rand_r(&consumerseed)%600+200;
        usleep(consumersleeptime);
        //wait on both semaphores
        //attempt to remove an item from buffer 
        //signal both semaphores
    }
}

我在程序顶部定义了静态整数生产者种子和消费者种子,作为全局变量。我这样做是因为我认为对 rand_r 的调用需要访问一个静态的、不变的内存位置。

这是正确的方法吗?还是每个线程都需要 不同的 整数。这会导致我的线程中出现任何竞争条件吗?生成的随机数呢——会不会每次都不一样?

编辑 1: 好的,所以这个问题的答案基本上是它不正确。每个线程都需要一个 唯一 种子整数。例如,这可以来自 time() 或 p_thread_self() id。我仍然对如何准确实现这一点感到困惑,但我会继续努力并报告。现在我决定使用 p_thread_self 作为每个线程的种子。

非常感谢您花时间查看/回答。

【问题讨论】:

    标签: c++ c multithreading random


    【解决方案1】:

    在 C++11 中,您可以使用 std::random_device 在每个线程上简单地创建另一个独立的种子。这与您在单线程代码中所做的完全相同。然后您可以在std::this_thread 上致电thread::sleep()

    #include <random>
    #include <thread>
    #include <chrono>
    
    thread_local std::random_device rd;    // used once only per thread to initialise RNG
    thread_local std::mt19937 rng(rd());   // thread-specific RNG
    std::uniform_int_distribution<int> uni(0,800);  // guaranteed unbiased
    
    // called possibly many times
    std::this_thread::sleep_for(uni(rng)*std::chrono::microseconds);
    

    特别是,没有必要(ab)使用当前时间作为种子和/或其他相关种子。

    【讨论】:

      【解决方案2】:
      1. 每个线程需要 1 个种子,而不是全局种子。
      2. 要为每个线程生成唯一的种子,请从主线程调用time() 以获取第一个种子。然后对于每个新线程,将一个添加到当前种子以获取下一个种子。例如,如果 time() 返回 100,您的种子将是 100、101、102 等。您的主线程需要将种子作为线程参数的一部分传递给每个线程。

      编辑:

      1. 如下面的 cmets 所示,您可以获取初始种子(上例中的 100)并将其与线程 ID 混合(使用异或或加法)。这样您就不必将种子传递给每个线程。至于 only 使用线程 id 作为种子,这也可以。但是,在新的运行中,您可能会获得与前一次运行相同的线程 ID(取决于您的操作系统如何确定线程 ID)。因此,如果您不想依赖操作系统生成线程 ID 的方式,您仍应使用一些初始种子,例如时间。

      【讨论】:

      • #2 的另一个选项是将线程 ID(从 pthread_self 或等效项获得)混合到基于时间的种子中。这消除了中心线程创建者了解种子的需要。
      • 使用thread_id作为每个线程的种子怎么样?我认为这将为每个线程创建一个独特的、大致随机的种子。我没有利用时间。这是我现在所做的,但我还没有测试过。
      • @Musicode 查看我的编辑。你说的会有用。它只是不如混音那么安全,因为您无法确定操作系统将为您分配什么线程 ID。
      • 这是有道理的。程序的重复运行可能会导致使用相同的种子。我认为这不会对我的程序的结果产生负面影响,但也许有些东西我没有看到,如果是这样,我可能必须为每个线程包含一个更随机的种子并包含时间。
      【解决方案3】:

      我的困惑源于这样一个事实,即如果我想要一个不同的整数 用于每个线程的 rand_r,那么我该怎么做呢?我怎样才能 如果我为每个线程使用不同的(即随机)整数 不能创建随机整数?

      这里你需要使用线程特定的变量。所以你可以使用这些方法。

      pthread_setspecific() & pthread_getspecific()
      

      这用于创建全局变量,但仍特定于每个线程(不共享):它们是线程特定的全局变量。

      这个例子有很好的https://stackoverflow.com/a/15101240/775964例子

      【讨论】:

      • 好吧,既然它们是随机变量,我就不能用时间之类的东西来代替吗?我还是一头雾水。
      • @Musicode 是的,你可以使用它但是为此目的,使用 rand() 是使用它的最复杂的方法。
      • pthread_getspecificpthread_setspecific 将仅在提供 POSIX 线程 API 的系统上可用。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-09
      • 2020-11-16
      • 2015-09-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多