Reputation: 281
I am new to multithreading in C and I had this question. I wrote the following code:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
pthread_mutex_t m=PTHREAD_MUTEX_INITIALIZER;
pthread_attr_t attr;
void* test(void *a)
{
int i=*((int *)a);
printf("The thread %d has started.\n",i);
pthread_mutex_lock(&m);
sleep(1);
printf("The thread %d has finished.\n",i);
pthread_mutex_unlock(&m);
pthread_exit(NULL);
}
int main()
{
int i=0;
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE);
pthread_t thread[5];
for (i=0;i<5;i++)
pthread_create(&thread[i],&attr,test,&i);
for (i=0;i<5;i++)
pthread_join(thread[i],NULL);
return 0;
}
Why do I get values like:
The thread 0 has started.
The thread 0 has started.
The thread 5 has started.
The thread 5 has started.
The thread 0 has started.
The thread 0 has finished.
The thread 0 has finished.
The thread 5 has finished.
The thread 5 has finished.
The thread 0 has finished.
or
The thread 1 has started.
The thread 2 has started.
The thread 5 has started.
The thread 4 has started.
The thread 0 has started.
The thread 1 has finished.
The thread 2 has finished.
The thread 5 has finished.
The thread 4 has finished.
The thread 0 has finished.
or even:
The thread 0 has started.
The thread 0 has started.
The thread 0 has started.
The thread 0 has started.
The thread 0 has started.
The thread 0 has finished.
The thread 0 has finished.
The thread 0 has finished.
The thread 0 has finished.
The thread 0 has finished.
etc, when I expected to get:
The thread 0 has started.
The thread 1 has started.
The thread 2 has started.
The thread 3 has started.
The thread 4 has started.
The thread 0 has finished.
The thread 1 has finished.
The thread 2 has finished.
The thread 3 has finished.
The thread 4 has finished.
Only when I put usleep(10)
after thread_create
do I get some "normal" values.
I compiled and run this code in Code::Blocks on Unix.
Upvotes: 2
Views: 107
Reputation: 372814
Notice that you are passing in the address of i
as a parameter to your threads:
pthread_create(&thread[i],&attr,test,&i);
This means that all of your threads will be reading the same variable i
to determine which thread they are. That is, all five threads will look at the same variable to determine their thread number. Consequently, as the value of i
increments in your for
loop, all the threads will perceive their thread number changing to use the new value of i
. This is why you're sometimes seeing 5 come up as the thread number, and also explains the fact that you're often skipping numbers or seeing far too many duplicates.
To fix this, you will need to give each thread their own copy of i
. For example, you could do something like this:
int* myI = malloc(sizeof(int));
*myI = i;
pthread_create(&thread[i], &attr, test, myI);
And then have the threads free the pointer before terminating:
void* test(void *a)
{
int i=*((int *)a);
printf("The thread %d has started.\n",i);
pthread_mutex_lock(&m);
sleep(1);
printf("The thread %d has finished.\n",i);
pthread_mutex_unlock(&m);
pthread_exit(NULL);
free(a);
}
Alternatively, you could cast i
to a void*
and pass that in:
pthread_create(&thread[i],&attr,test, (void*)i);
If you do this, you would then have the threads cast their arguments directly back to int
, not to int*
:
void* test(void *a)
{
int i = (int)a;
printf("The thread %d has started.\n",i);
pthread_mutex_lock(&m);
sleep(1);
printf("The thread %d has finished.\n",i);
pthread_mutex_unlock(&m);
pthread_exit(NULL);
}
Hope this helps!
Upvotes: 2
Reputation: 182649
You're passing the address of a variable the for
is changing (i
) so you're at the mercy of the scheduler. You should just pass a copy. As a cheap, not completely-kosher way:
pthread_create(&thread[i],&attr,test, (void*)i);
/* ... */
int i = (int)a;
Upvotes: 5