I wrote this piece of code
#include /* Input/Output */
#include /* General Utilities */
#include
unsigned int cnt=0;
is share-able among the threads and operation ++
is not atomically increase cnt
. Two thread may read same values of cnt
and increase, and overwrites cnt
. You need to apply some concurrency control mechanism like semaphore, or mutex.
If you would disassemble code using following command (suppose code name is thread1.c
)
~$ gcc thread.c -lpthread -S
The output assembly code name is thread1.s
.
your wil find cnt++
is more then one instruction in low level in your code:
movl $0, -12(%ebp)
jmp .L2
.L3:
movl cnt, %eax
addl $1, %eax
movl %eax, cnt
addl $1, -12(%ebp)
.L2:
movl NITERS, %eax
(1) cnt
fist move to %eax
(2) then add one to %exc
(3) move %eax
into cnt
back
And due to thread context switching in between this line, same value of cnt
is read by more than one threads. hence cnt++
is not atomic.
Note: Global variable are thread shareable like cnt
, and local variable like i
that you declared in count()
is thread specific.
I modified your code and imposed concurrency control using semaphore, now it would work fine.
Only modified code shown
#include <pthread.h> /* POSIX Threads */
#include <semaphore.h>
unsigned int cnt=0; /*Count variable%*/
const int NITERS=1000;
sem_t mysem;
void count()
{
int i=0;
for(i=0; i<NITERS; i++)
{
sem_wait(&mysem);
cnt++;
sem_post(&mysem);
}
pthread_exit(0);
}
int main()
{
if ( sem_init(&mysem,0,1) ) {
perror("init");
}
// rest of your code
}
This will work good! some examples:
nms@NMS:~$ ./thread
OK! cnt=2000
nms@NMS:~$ ./thread
OK! cnt=2000
nms@NMS:~$ ./thread
OK! cnt=2000
Your 2 threads access a shared resource without protection thus a race conditions apply. The increase operation isn't atomic, so you can actually have something like this in terms of machine operations:
Thread 1 Thread 2
Load value of cnt
Load value of cnt
Increase value of cnt
Write value of cnt Increase value of cnt
Write value of cnt
Note, that although both threads have increased the cnt
, it was actually increased only by 1.
If you want the result to be deterministic, you need to protect the shared resource (cnt
), e.g. by locking it prior to access.
You have a race condition problem.More info (I know it talks about visual basic, just skip these things)here.
To solve it you need a mutex, declare it as global variable:
pthread_mutex_t mux;
Initialize it:
pthread_mutex_init(&mux,NULL);
Then use it for reading the shared variable:
void count()
{
int i=0;
for(i=0; i<NITERS; i++)
{
pthread_mutex_lock(&mux);
cnt++;
pthread_mutex_unlock(&mux);
}
pthread_exit(0);
}
All this because there are two threads incrementing the same variable, they fetch the variables before incrementing it and they put it in a register.So they value that they are reading isn't unique: every thread has it's own copy so each thread may ignore the change of the other, until they effectively write it to it's address in the memory.
NB : If you want that the thread update the variable in an ordered way (i.e.: thread 1 counts to NITERS without being interrupted, when thread 2 starts counting), you have to lock the mutex before the for.
The increment operator is usually implemented by a read-modify-write, which is non-atomic.
A non-atomic read-modify-write across threads can sometimes do this:
Thread 1: Thread 2: count
Read count ... 1
Add 1 Read count 1
Write count Add 1 2
... Write count 2
Resulting in the count being less than expected.
If you will access a shared resource across multiple threads, you need to protect it with some kind of threading aware locking mechanism, such as a mutex.