1

I have a code to simulate serial vs parallel computing. However, it seems has a race condition.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

void *foo(void *para) {
    printf("This is Thread %d running...\n", *(size_t *)para);
    fflush(stdout);
    sleep(2);
}

int main(void)
{
    /* loop version
    for (size_t i = 0; i < 4; i++) {
        foo((void *)&i);
    }
    */

    pthread_t pool[4];
    for (size_t i = 0; i < 4; i++) {
        pthread_create(&pool[i], NULL, foo, (void *)&i);
    }

    for (size_t i = 0; i < 4; i++) {
        pthread_join(pool[i], NULL);
    }

    return 0;
}

Output:

[william@Notebook Downloads]$ time ./a.out 
This is Thread 1 running...
This is Thread 2 running...
This is Thread 4 running...
This is Thread 3 running...

real    0m2.003s
user    0m0.003s
sys     0m0.000s

[william@Notebook Downloads]$ time ./a.out 
This is Thread 3 running...
This is Thread 3 running...
This is Thread 2 running...
This is Thread 4 running...

real    0m2.003s
user    0m0.003s
sys     0m0.000s

Most of examples when race condition happens is whenn you have multiple threads try to write a shared value. However, there is no write operation in this code at all. So, why this happens? Is it because for loop is not excuted in serial order?

user762750
  • 159
  • 7

2 Answers2

3

I didn't event read your question. I just looked at your code and observed observed a huge problem that is likely the root cause of whatever you are asking about. Look at the following loop code:

pthread_t pool[4];
for (size_t i = 1; i <= 4; i++) {
    pthread_create(&pool[i], NULL, foo, (void *)&i);
}

You have an "array" of size 4, but you are iterating over indices [1..4]. C and C++ arrays start at index 0 and end at (len-1). In other words, you should be iterating over indices [0..3]. Inserting anything into pool[4] is going create undefined behavior.

Instead of this line:

for (size_t i = 1; i <= 4; i++) {

You want this:

for (size_t i = 0; i < 4; i++) {

Apply the same fix to the second loop performing the pthread_join statements.

Also passing &i as the thread parameter is causing additional undefined behavior. Your threads are accessing the address of a variable that is constantly changing by the main thread and then going out of scope.

selbie
  • 100,020
  • 15
  • 103
  • 173
  • It still happens even when I change loop to ```for (size_t i = 0; i < 4; i++) {``` – user762750 May 26 '19 at 03:09
  • Also passing `&i` as the thread parameter is causing additional undefined behavior. Your threads are accessing the address of a variable that is constantly changing by the main thread and then going out of scope. – selbie May 26 '19 at 03:12
  • Thanks, so is there any way to workaround it? – user762750 May 26 '19 at 03:19
  • Yes - pass `(void*)i` instead of `&i` and cast back to a size_t in the thread proc – selbie May 26 '19 at 03:24
3

While the earlier answer addresses a problem with your code, it's not the cause of the problem you are asking about as its author assumed.

The problem is that you are passing the same argument to every thread. The threads are printing whatever i happens to be at the time, a value you keep changing. i might not even exist at the time the thread tries to access it.

Instead of passing the same pointer each time, you'll need to pass different pointers (e.g. something allocated just for the thread, or pointers to different elements of an array that's guaranteed to outlive the threads). Alternatively, since the pointer itself is copied, you could cast an sufficiently small integer into pointer and have the thread cast the pointer back into an integer.

ikegami
  • 367,544
  • 15
  • 269
  • 518