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.