Reputation: 85
I'm new to pthreads on c, so I'm just trying to do some basic program with two threads that increments an integer till it equals 10000, then every threads writes how much time it has incremented the integer, and the principale thread writes the final result, on my output the final result is fine (always equals to 10000) but the incrementation time of each thread is wrong, can someone explains to me why is that happening ?
my code:
//Threads in C
/* Includes */
#include <unistd.h> /* Symbolic Constants */
#include <sys/types.h> /* Primitive System Data Types */
#include <errno.h> /* Errors */
#include <stdio.h> /* Input/Output */
#include <stdlib.h> /* General Utilities */
#include <pthread.h> /* POSIX Threads */
#include <string.h> /* String handling */
void *T1 (void * par){
int * cp = (int*)(par);
printf("Thread 1 begin, counter equals %d \n",*cp);
int f=0;
while((*cp)< 10000) {++(*cp);
f++;}
printf("Thread 1 finished, i've incremented %d times \n",f);
pthread_exit(NULL);
}
void *T2 (void * par){
int * cp = (int*)(par);
printf("Thread 2 begin, counter equals %d \n",*cp);
int j=0;
while((*cp)< 10000) {++(*cp);
j++;}
printf("Thread 2 finished, i've incremented %d times \n",j);
pthread_exit(NULL);
}
int main(){
pthread_t idT1, idT2;
int counter = 0;
if (pthread_create(&idT1, NULL, T1, &counter) != 0)
printf("erreur creation");
if (pthread_create(&idT2, NULL, T2, &counter) != 0)
printf("erreur creation");
pthread_join(idT1, NULL);
pthread_join(idT2, NULL);
printf(" Total = %d",counter);
return 0;
}
a sample output:
Thread 1 begin, counter equals 0
Thread 2 begin, counter equals 0
Thread 1 finished, i've incremented 10000 times
Thread 2 finished, i've incremented 8602 times
Total = 10000
Upvotes: 2
Views: 128
Reputation: 1193
When using 2 threads concurrently, a thread can be paused and the kernel makes a context switch to the other thread at anytime, we can not predict the order of code execution between the 2 threads. For simple, I will show you a scenario can happen with single core CPU.
In x86, the counter is increased by some instructions like this: ++(*cp);
movl (0x80123468), %ecx
addl $1, %ecx
movl %ecx, (0x80123468)
&counter = (0x80123468)
Imagine Thread 1 enters this pice of code, counter is going to be increased by one. 1st instruction is to load current value of counter(assume that it's 200) into register eax. Thus, eax=200 for Thread 1. Then 2nd instructions adds one to the register; thus eax=201.
Now something happens that makes the kernel stop Thread 1 operation(e.g. an interrupt), then switch context to Thread 2 to run, and it enters the same piece of code. executing the 1st instruction, loading the value of counter and putting it into its own eax(each thread has it's own registers), Thread2-eax =200, then execute the 2nd instruction, Thread2-eax = 201. Then executing the 3rd instruction to store value 201 into &counter (0x80123468).
After that, a context switch occurs, Thread2 is paused and Thread1 runs again, it continues executes the 3rd instruction, and store Thread1-eax value = 201 into &counter (0x80123468).
As you can see, when this scenario happens, counter is only increased by 1 while both j and f are increased by 1(originally, we expect only one of them is increased by one, not both)
Your while loop breaks if counter = 10000, so the counter can be correct, the counter is increased 10000 times(not always, I see a scenario it can be 10001, however it seems rarely happens, you can try figuring it out yourself by examining assembly code of ((*cp)< 10000)
). however we do not know how many time j and f are increased?
Upvotes: 3
Reputation: 180103
Your program has a data race because your two threads access the same shared variable without synchronization, and some of the accesses are writes. The program's behavior is therefore undefined. You need to protect each thread's accesses to the shared counter (both reads and writes) by appropriate use of a mutex, semaphore, or other effective synchronization mechanism. Alternatively, you could use a counter of atomic type.
Upvotes: 1