Using stdlib's rand() from multiple threads

2019-01-03 17:33发布

I have several threads which all run the same function. In each of these they generate a different random number several times. We tried to do this by putting srand(time(0)) at the start of the function, but it seems that they all get the same number.

Do we need to call srand(time(0)) only once per program, i.e at the start of main (for example), at the start of each function that is called several times, or something else?

7条回答
萌系小妹纸
2楼-- · 2019-01-03 17:50

That's a good question. I can't directly answer it because I think there are bigger issues. It doesn't even seem to be clear that rand is thread safe at all anyway. It maintains internals state and it doesn't seem to be well defined if that's per process or per thread, and if it's per process if it's thread safe.

To be sure I would lock a mutex around each access.

Or preferably use a better defined generate such as one from boost

查看更多
小情绪 Triste *
3楼-- · 2019-01-03 17:51

They all get the same number because you presumably start all the threads at the same time, or they are all using the same static seed, in which case you are a bit stuffed. You need a better source of entropy than time(). However, a quick hack would be to seed with (time * thread-id) where thread-id is the id of each worker thread.

Fo course, the correct solution in C++ is is not to use random number generator functions, but to use random number generator objects, like those provided by the Boost random number library, which by their very nature (because they are stack based) are thread-safe. See this answer I prepared earlier for an example. However, there may still be a problem providing sufficient entropy in an MT program, as using time() will still have the problem I mentioned above.

查看更多
贼婆χ
4楼-- · 2019-01-03 17:52

From the rand man page:

The function rand() is not reentrant or thread-safe, since it uses hidden state that is modified on each call.

So don't use it with threaded code. Use rand_r (or drand48_r if you're on linux/glibc). Seed each RNG with a different value (you could seed a first RNG in the main thread to produce random seeds for the ones in each thread).

查看更多
Viruses.
5楼-- · 2019-01-03 17:54

srand() seeds the random number generator. You should only have to call srand(time(NULL)) once during startup.

That said, the documentation states:

The function rand() is not reentrant or thread-safe, since it uses hidden state that is modified on each call. This might just be the seed value to be used by the next call, or it might be something more elaborate. In order to get reproducible behaviour in a threaded application, this state must be made explicit. The function rand_r() is supplied with a pointer to an unsigned int, to be used as state. This is a very small amount of state, so this function will be a weak pseudo-random generator. Try drand48_r(3) instead.

The emphasized part of the above is probably the reason why all your threads get the same number.

查看更多
Evening l夕情丶
6楼-- · 2019-01-03 17:57

If you are launching the threads all at the same time, the time sent to srand is probably the same for each thread. Since they all have the same seed, they all return the same sequence. Try using something else like a memory address from a local variable.

查看更多
成全新的幸福
7楼-- · 2019-01-03 18:01

As you are using C++, rather than C, you may be able to avoid the threading problems often associated with srand/rand by using c++11. This depends on using a recent compiler which supports these features. You would use a separate engine and distribution on each thread. The example acts like a dice.

#include <random>
#include <functional>

std::uniform_int_distribution<int> dice_distribution(1, 6);
std::mt19937 random_number_engine; // pseudorandom number generator
auto dice_roller = std::bind(dice_distribution, random_number_engine);
int random_roll = dice_roller();  // Generate one of the integers 1,2,3,4,5,6.

I referred to Wikipedia C++11 and Boost random when answering this question.

查看更多
登录 后发表回答