I am new to pthread and try to understand how they work. The goal is to write a program that creates a main stream. Each new thread created creates 2 new threads. In this case, the threads should receive their id and time to wait (from 1 to 10 seconds) as parameters. New flows are created until they reach a maximum (determined by a constant). In my program, threads are created correctly. The problem is that id is not unique - each id is printed twice. For example:

 t0: Hello my id is 0 t2: Hello my id is 2 t2: Hello my id is 2 t4: Hello my id is 4 t4: Hello my id is 4 

I assume that the problem lies in the general pointer to an array of parameters, but I do not know to prevent this. I block access to the array with the help of mutex and it seems like there should be no race condition. But still, the result is the id is incorrect.

 #include <stdio.h> #include <pthread.h> #include <stdlib.h> #define MAX_THREADS 10 pthread_mutex_t threads_count_mutex; pthread_mutex_t args_mutex; int wait_time_overall = 0; int threads_count = 0; void* SpawnTwoThreads(void *args) { pthread_t t1; pthread_t t2; int* thread_args = (int*)args; printf("Sleeping for %d befor thread creation\n", thread_args[1]); sleep(thread_args[1]); printf("t%d: Hello, my id is %d\n", thread_args[0], thread_args[0]); if (threads_count < MAX_THREADS) { pthread_mutex_lock(&threads_count_mutex); threads_count++; pthread_mutex_unlock(&threads_count_mutex); pthread_mutex_lock(&args_mutex); thread_args[0]++; thread_args[1] = rand() % 10; pthread_mutex_unlock(&args_mutex); pthread_create(&t1, NULL, SpawnTwoThreads, thread_args); } if (threads_count < MAX_THREADS) { pthread_mutex_lock(&threads_count_mutex); threads_count++; pthread_mutex_unlock(&threads_count_mutex); pthread_mutex_lock(&args_mutex); thread_args[0] = rand() % 10; thread_args[1]++; pthread_mutex_unlock(&args_mutex); pthread_create(&t2, NULL, SpawnTwoThreads, thread_args); } pthread_join(t1, NULL); pthread_join(t2, NULL); pthread_exit(NULL); } int main(void) { pthread_t t1; srand(time(NULL)); pthread_mutex_init(&threads_count_mutex, NULL); pthread_mutex_init(&args_mutex, NULL); int start_args[2]; start_args[0] = 0; start_args[1] = rand() % 10; pthread_create(&t1, NULL, SpawnTwoThreads, start_args); printf("In main: waiting for all threads to complete\n"); pthread_join(t1, NULL); printf("Overall waittime is %d\n", wait_time_overall); pthread_exit(NULL); } 

    1 answer 1

    First, the race is still there.

    Here the variable is read.

     if (threads_count < MAX_THREADS) { 

    here it is written

     pthread_mutex_lock(&threads_count_mutex); threads_count++; pthread_mutex_unlock(&threads_count_mutex); 

    Yes, reading must also be wrapped in mutex. This variable a couple of times in the code is not lucky :).

    But the problem is that thread_args is passed by pointer. And this seems not at all what you wanted. It is enough to make a copy of this structure before creating the stream, as everything falls into place. Plus, it seems there is a copy-paste in SpawnTwoThreads - there are two very similar thread creation codes, but the second one is the opposite. But it may be so conceived.

    • I thought in mutex only need to wrap the area where it is written. Is it necessary then to also wrap each output through printf in mutex? This is also a reading. At the expense of reading in the if: is it then necessary to block the entire if-block using mutex? At the expense of arguments in the second block - corrected. Thank. Ie I can create a new array using memcpy and transfer the link to it to the stream, I understand correctly? - Evhenii Vasylenko
    • one
      in this case, theoretically, reading problems should not arise - an int can be written in one instruction. But if the structure is a bit more complicated, then it can be read while it is being written elsewhere and in an inconsistent state. And this is sadness. But to wrap everything in a mutex is also a bust. Ie I can with the help of memcpy - yes there are two int copy. - KoVadim
    • Using copies solved the problem. Was it possible to solve this problem more elegantly, provided that both parameters still need to be passed? - Evhenii Vasylenko
    • What do you mean by "more elegant"? - KoVadim
    • one
      You can use atomics or semaphores. Or simply do not create threads. - KoVadim