51

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?

user7116
  • 63,008
  • 17
  • 141
  • 172
SIMEL
  • 8,745
  • 28
  • 84
  • 130
  • 4
    You are probably better off with the new random number generators coming in C++0x. What compiler are you using? – fredoverflow May 28 '11 at 11:37
  • What OS are you using windows/linux ?? – ob_dev May 28 '11 at 11:45
  • if all the treads use the same srand() you will get the same random numbers – ob_dev May 28 '11 at 11:46
  • Don't call rand() from multiple threads. Use the random number generators in C++0x. These are also available in Boost. – Johan Råde May 28 '11 at 13:18
  • I've observed that rand() starts over the same sequence everytime a thread starts. In my application I start one thread in a loop, and rand() repeated the same sequence in each iteration. rand() is definitely a no-no with multithreading. I fixed this by using those newer C++ generators like suggested above. – Paulo Carvalho Jul 14 '19 at 11:25

6 Answers6

47

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.

Frédéric Hamidi
  • 258,201
  • 41
  • 486
  • 479
  • 1
    It is not clear how this very narrowly implementation-specific quote applies to the original question. The latter does not state what implementation is used. `rand()` can easily be thread safe. It is up to a specific implementation. – AnT stands with Russia Sep 07 '18 at 09:09
  • 4
    @AnT: But `rand()` is not *required* to be thread-safe, so a portable multi-threaded program cannot safely use it (except by protecting it with a mutex or similar). C17 7.22.2.1p3: "The `rand` function is not required to avoid data races with other calls to pseudo-random sequence generation functions [such as itself]". – Nate Eldredge Oct 16 '21 at 07:00
11

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.

kshepherd
  • 778
  • 9
  • 15
8

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

Mat
  • 202,337
  • 40
  • 393
  • 406
  • Interestingly, `rand` seems to be a thread-safe version of `rand_r` in glibc, not vice-versa (if I am interpreting [the source code](https://github.com/lattera/glibc/blob/895ef79e04a953cac1493863bcae29ad85657ee1/stdlib/random.c#L293) correctly). – vgru Aug 19 '20 at 12:52
  • You are not. You are looking at exactly the internal state that makes it non thread-safe. – borrrden Oct 10 '20 at 02:20
7

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.

Peter Heath
  • 103
  • 1
  • 1
  • 5
    Using the memory address of a local variable is not a good source of entropy. – Jonathon Reinhart Sep 27 '15 at 17:32
  • Using a different seed is inadequate. Since `srand` and `rand` are not thread-safe, they may share state data. In such case, calling `srand` in one thread will set it for all threads, and, additionally, data-race errors may occur. – Eric Postpischil Apr 22 '23 at 13:55
3

C was not designed for multithreading, so behavior of srand() with multithreading is not defined and depends on the C runtime library.

Many Unix/Linux C runtime libraries use single static state, which is not safe to access from multiple threads, so with these C runtimes you can't use srand() and rand() from multiple threads at all. Other Unix C runtimes may behave differently.

Visual C++ runtime uses per-thread internal state, so it is safe to call srand() for each thread. But as Neil pointed out, you will likely seed all threads with same value - so seed with (time + thread-id) instead.

Of course, for portability, use Random objects rather than rand function, and then you would not depend on hidden state at all. You still need one object per thread, and seeding each object with (time + thread-id) is still a good idea.

Michael Entin
  • 7,189
  • 3
  • 21
  • 26
  • I'm going to comment "windows" just so that people more readily land on this answer when using MS Visual C++ in a Windows context. – omatai Mar 11 '21 at 00:54
2

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

jcoder
  • 29,554
  • 19
  • 87
  • 130