Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Random function in multi-threaded c program

PLease see the whole question

I know that srand() should be called only once, but my 2nd code segment shows that that does not solve the issue!!!!

The program I have written is giving me outputs which I can't quite make out why is it so. Different alterations of code segments give different outputs.

Objective of code:
The code uses omp to simply run a piece of code for 3 threads. Each thread has to print 3 random values using the rand() function. So, a total of 9 outputs would come. Thread 0 is the main thread/ the main program's run flow. Thread 1 and Thread 2 are the fellow new threads created at the start of code for the threads.
The code:

#include<omp.h>
#include<stdio.h>
#include<stdlib.h>
#include<time.h>

int main()
{


     #pragma omp parallel num_threads(3)
    {
        srand(time(NULL));
        int i=0;
        for(i=0;i<3;i++)
        {
            printf("\nRandom number: %d by thread %d", rand(), omp_get_thread_num());
        }
    }

    return 0;
}


The output:

Random number: 17105 by thread 0
Random number: 30076 by thread 0
Random number: 21481 by thread 0
Random number: 17105 by thread 1
Random number: 30076 by thread 1
Random number: 21481 by thread 1
Random number: 17105 by thread 2
Random number: 30076 by thread 2
Random number: 21481 by thread 2



But if I make keep the srand(time(NULL)) before the code for thread like,

 srand(time(NULL));  
 #pragma omp parallel num_threads(3)
{
    int i=0;
    ......
    ......(rest is same)

The output is, The output:

Random number: 16582 by thread 0
Random number: 14267 by thread 0
Random number: 14030 by thread 0
Random number: 41 by thread 1
Random number: 18467 by thread 1
Random number: 6334 by thread 1
Random number: 41 by thread 2
Random number: 18467 by thread 2
Random number: 6334 by thread 2



The Problem, and my doubts:

  • By placing the `srand` outside, all the threads' 1st call to `rand()` gave the same random number, all of their 2nd call gave the same random number, and similarly for their 3rd call also.
  • By placing the `srand` inside, the main thread's calls resulted in different random numbers than the others. BUT, the 2 new other threads among them gave the same random number for their respective calls to `rand()`.

So,

  • What is actually happening here? How come the placement of the `srand()` function make a difference only to the main thread (thread `0`)?
  • Why is it that either ways the the other 2 new threads always output same random number for the respective call to `rand()`?
  • How is this `srand()` and `rand()` even linked, to cause this abnormality?
  • And I tried giving wait intervals to each thread to remove that possibility of the `rand()` function being called by different threads at the same time, which might result in same random number maybe. But the problem was exactly like before. No change in the output (just the time at which output occurred was different).

Please help me understand this whole thing..

like image 356
powersource97 Avatar asked Feb 05 '16 18:02

powersource97


1 Answers

Updated: Inserted direct answers to the OP's enumerated questions.

What is actually happening here?

Although some versions of the rand() function may be "thread safe" in some sense, there is no reason to believe or expect that without any external memory synchronization, the set of values returned by multiple rand() calls executed by different threads will be the same as the set of values returned by the same number of calls all executed by one thread. In particular, rand() maintains internal state that is modified on each call, and without any memory synchronization, it is entirely plausible that one thread will not see updates to that internal state that are performed by other threads. In that case, two or more threads may generate partially or wholly the same sequence of numbers.

How come the placement of the srand() function make a difference only to the main thread (thread 0)?

The only thing that can be said for certain is that if the srand() is outside the parallel block then it is executed only by the main thread, whereas if it is inside then it is executed separately by each thread. Inasmuch as your code is not properly synchronized, the effects of each case are not predictable from the source code, so my next comments are mostly speculative.

Supposing that time(), with its (only) one-second precision, returns the same value in each thread, placing srand() inside the parallel region ensures that every thread sees the same initial random number seed. If they then do not see each other's updates then they will generate the same sequences of pseudo-random numbers. Note, however, that you can neither safely rely on the threads seeing each other's updates nor safely rely on them not seeing each others updates.

If you put the srand() outside the parallel region, however, so that it is executed only by the main thread, then there are additional possibilities. If OMP maintains a thread pool whose members are already started before you enter the parallel section, then it may be that threads 1 and 2 fail to see the effect of thread 0's srand() call at all, and therefore both proceed with the default seed. There are other possibilities.

Why is it that either ways the the other 2 new threads always output same random number for the respective call to rand()?

It's impossible to say with any certainty. I'm inclined to guess, however, that none of the threads involved see each other's updates to rand()'s internal state.

How is this srand() and rand() even linked, to cause this abnormality?

The two functions are intimately linked. The purpose of srand() is to modify rand()'s internal state (to "seed" it, hence the "s" in "srand"), so as to start the psuedo-random number sequence it generates at a different (but still deterministic) point.


This problem can be solved in the same way that any problem involving multi-threaded access to shared variables can be solved: by applying synchronization. In this case, the most straightforward form of synchronization would probably be to protect rand() calls with a mutex. Since this is OMP code, your best bet might be to use OMP locks to implement a mutex, as it seems dicey to mix explicit pthreads objects with OMP declarations.

like image 51
John Bollinger Avatar answered Oct 26 '22 00:10

John Bollinger