Question

I have a multithreaded C++ application which needs many random numbers in each thread.

Before the C++11 I used a "randomizer" which generates some randoms in the main thread with the rand() function and it passes different randoms to each subthread. Then, all the subthreads join the main thread, the randomizer is used again and new subthreads are called and so on for N times.

Now, I would like to replace rand() with the C++11 in order to avoid the randomizer and to generate random numbers in each thread.

I would like to seed the random number generator such that: * the sequence of seeds changes from run to run * the sequence of random numbers in each thread is different from thread to thread (also if the thread are called in different loops)

I thought seeding like that:

mt19937 rng;
rng.seed(this_thread::get_id().hash());

which is good for me since this_thread::get_id().hash() is a "random" number, but sometimes, in different loops, I could get the same ids.

How could I seed randomly the random number generator in order to avoid getting the same sequence of random numbers in some threads?

Was it helpful?

Solution

Since the thread IDs are different, perhaps just bump the values using the clock:

#include <chrono>
...

typedef std::m19937::result_type seed_type;

typename std::chrono::system_clock seed_clock;
auto init_seed = static_cast<seed_type>
    (seed_clock.now().time_since_epoch().count());

init_seed += static_cast<seed_type>(this_thread::get_id());

rng.seed(init_seed);

OTHER TIPS

The appropriate default for seeding is:

std::random_device r;
std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()};
std::mt19937 eng(seed);

You could simply do this on each thread and it should work:

#include <algorithm>
#include <functional>
#include <iostream>
#include <iterator>
#include <mutex>
#include <random>
#include <thread>

int main() {
    std::mutex iomutex;

    std::vector<std::thread> threads;

    for (int i = 0 ; i < 10; ++i) {
        threads.emplace_back([&iomutex](int tid) {
            std::random_device r;
            std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()};

            std::mt19937 eng(seed);
            std::uniform_int_distribution<> dist(1, 100);

            std::lock_guard<std::mutex> ioguard(iomutex);
            std::cout << "Thread " << tid << ": ";
            std::generate_n(std::ostream_iterator<int>(std::cout, " "), 10, std::bind(dist, eng));
            std::cout << '\n';
        }, i);

    }
    for (auto &&t : threads) {
        t.join();
    }
}

Alternatively you could compute the seeds on the main thread and pass each worker the data to initialize its engine. Unfortunately you can't pass a seed_seq, so in the example below I simply pass an initialized engine.

int main() {
    std::mutex iomutex;
    std::vector<std::thread> threads;
    std::random_device r;

    for (int i = 0 ; i < 10; ++i) {
        std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()};
        std::mt19937 thread_eng(seed);

        threads.emplace_back([&iomutex](int tid, std::mt19937 init_eng) {
            std::mt19937 eng(std::move(init_eng));
            std::uniform_int_distribution<> dist(1, 100);

            std::lock_guard<std::mutex> ioguard(iomutex);
            std::cout << "Thread " << tid << ": ";
            std::generate_n(std::ostream_iterator<int>(std::cout, " "), 10, std::bind(dist, eng));
            std::cout << '\n';
        }, i, thread_eng);

    }
    for (auto &&t : threads) {
        t.join();
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top