The way you can use a pthread_mutex
to solve is problem is to enforce all threads that perform the operation
unsplitable action: "data read, modify and write" to use the same mutex.
The mutex has to be taken before performing this critical section and would be released later, after modifying the data.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | // compile with $ gcc -Wall -g *.c -pthread -o program // run with ./program // check with valgrind --tool=helgrind ./program #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> pthread_mutex_t mutex; void* thread_run(void* data) { int i_data; sleep(2); printf("[TH_ID:%ld]: Hello from the thread \n", pthread_self()); pthread_mutex_lock(&mutex); printf("[TH_ID:%ld]: Reading %i \n", pthread_self(),(*(int*)data)); i_data=(*(int*)data); sleep(1); //It should be removed i_data++; (*(int*)data)=i_data; printf("[TH_ID:%ld]: Writing %i \n", pthread_self(),(*(int*)data)); pthread_mutex_unlock(&mutex); printf("[TH_ID: %ld]: To exit...............\n",pthread_self()); return data; } int main() { int i; pthread_t thread[2]; int data=0; int thread_rc; if(pthread_mutex_init(&mutex,NULL)!=0) return -1; for (i=0;i<2; i++) { printf("[MAIN:%ld]: Starting............ \n",pthread_self()); if ((thread_rc=pthread_create(&thread[i],NULL,thread_run,&data))!=0) { printf("Error creating the thread. Code %i",thread_rc); return -1; } } sleep(1); printf("[MAIN:%ld]: Thread allocated \n",pthread_self()); int *ptr_output_data; for ( i=0;i<2; i++) { pthread_join(thread[i],(void **)&ptr_output_data); } pthread_mutex_destroy(&mutex); printf("[MAIN:%ld]: Thread returns %d \n",pthread_self(), *ptr_output_data); return 0; } |
In this example you can see that thread execution always returns the same result in the terminal:
[MAIN:1]: Starting............ [MAIN:2]: Starting............ [MAIN:2]: Thread allocated [TH_ID:0]: Hello from the thread [TH_ID:0]: Reading 0 [TH_ID:0]: Writing 1 [TH_ID:0]: To exit............... [TH_ID:1]: Hello from the thread [TH_ID:1]: Reading 1 [TH_ID:1]: Writing 2 [TH_ID:1]: To exit............... [MAIN:2]: Thread returns 2
The list of execution that causes the use of lock that makes reading and writing of data from different threads is not performed concurrently includes the following executions:
T_0 lock(m), T_1 lock(m), T_0 read_(0), T_0 write_(1), T_0 unlock(m), T_1 read_(1), T_1 write_(2), T_1 unlock(m)
T_1 lock(m), T_0 lock(m), T_1 read_(0), T_1 write_(1), T_1 unlock(m), T_0 read_(1), T_0 write_(2), T_0 unlock(m)
And one that should never occur, since the locks avoid this execution, is:
T_0 lock(m), T_1 lock(m), T_0 read_(0), T_1 read_(0), T_0 write_(1), H_1 write_(1), T_0 unlock(m), T_1 unlock(m)
This situation never occurs because the mutex prevents two threads take the mutex simultaneously.