Reputation: 333
I want to create two threads, which are supposed to look like this:
P1:
while(1) {
printf("1");
printf("2");
printf("3");
printf("4");
}
return NULL;
P2:
while(1) {
printf("5");
printf("6");
printf("7");
printf("8");
}
return NULL;
According to my knowledge of parallel threads it won't print 12345678 but a completely random variation of the numbers due to lack of synchronization.
However when I try to replicate it in real code it keeps printing 1234 a few times, then switches to 5678, prints it a few times and goes back to 1234.
Is my understanding of threads wrong or is my code not equivalent to the problem?
void *print1(void *arg) {
while(1) {
printf("1");
printf("2");
printf("3");
printf("4\n");
}
return NULL;
}
void *print2(void *arg) {
while(1){
printf("5");
printf("6");
printf("7");
printf("8\n");
}
return NULL;
}
int main() {
pthread_t tid1, tid2;
pthread_create(&tid1, NULL, print1, NULL);
pthread_create(&tid2, NULL, print2, NULL);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
return 0;
}
Upvotes: 4
Views: 281
Reputation: 4411
While accessing shared data inside multi threaded applications it is always very important to protect shared data. In your case you have two threads accessing the standard output simultaneously, therefore you have a race condition wherein two threads race to acquire stdout
. Therefore I've taken the liberty to adapt you program in to a case where the standard output is protected by a mutex. Just before the the stdout is used we lock a mutex, the other thread will also try to lock the mutex, but both can't do that in the same time simultaneously. Thus the 2nd thread that tries to lock is put to sleep.
And as others have told you it might seem that the program you originally wrote made nice outputs, that more or less coincidence, on other systems there is no way you can guarantee that and the output of the two threads can mix up. ending in output like
1256734
8
5671234
8
etc
Now you might think that once the mutex is unlocked the other will automatically take over, but that is up to the scheduler. Therefore in the example below the program might do several 1234 before outputting 5678. But it will always be either 1234 or 5678 and those outputs aren't mixed. If you really want to alternate the two thread you have to more sophisticated output and lookup conditional variable for example, pthread API supports those as wel.
#include<pthread.h>
#include<stdio.h>
void *print1(void *arg) {
pthread_mutex_t* lock = arg;
while(1) {
/*now we do output from one thread while we lock the
*mutex from the main function.*/
pthread_mutex_lock(lock);
printf("1");
printf("2");
printf("3");
printf("4\n");
fflush(stdout);
/*now we must lock the lock, because otherwise
*the other thread can't lock the mutex and if
*we would try to lock the mutex again we would
*create a deadlock, hower recursive locking is
*possible, but beyond the scope of my anwer.*/
pthread_mutex_unlock(lock);
}
return NULL;
}
void *print2(void *arg) {
/*see the note in the frist thread*/
pthread_mutex_t* lock = arg;
while(1){
pthread_mutex_lock(lock);
printf("5");
printf("6");
printf("7");
printf("8\n");
fflush(stdout);
pthread_mutex_unlock(lock);
}
return NULL;
}
int main() {
pthread_t tid1, tid2;
/*lets create a lock to protect our shared resources
*in this case the standard output.
*/
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
/*make sure that the mutex is available inside the thread
*by sending a pointer to the mutex as fourth argument to
*phtread_create.*/
pthread_create(&tid1, NULL, print1, &lock);
pthread_create(&tid2, NULL, print2, &lock);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
return 0;
}
Upvotes: 1
Reputation:
Is my understanding of threads wrong or is my code not equivalent to the problem?
Neither. Your two threads just happen to be scheduled in the expected way on your system. There are many reasons for the apparent synchronisation, but it is fairly unpredictable as the OS scheduler will pause one or more threads when the system gets busy. You might try running a heavy workload across multiple threads at the same time as running your example program and see if things get confused.
This is the reason for thousands of unexpected bugs in multithreaded programs, and the reason we need mutexes, semaphores, atomic writes and sophisticated thread debugging tools like helgrind or its equivalent. When dealing with thread-shared data, proving there are no deadlocks or other syncronisation issues is extraordinarily difficult thing to do.
Upvotes: 3
Reputation: 29794
You can't rely on timing assumptions when multithreading.
An explanation for this question is that the sequence of printf
s inside your loop take so little time to execute that can be executed inside the quantum of time given to the thread.
Upvotes: 5