I am working on a project and trying to use pthread_cond_wait() and pthread_cond_signal() to synchronize two threads.
My code looks something like this:
pthread_mutex_t lock_it = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t write_it = PTHREAD_COND_INITIALIZER; int main(int argc, char**argv) { pthread_t t_send_segments, t_recv_acks; pthread_create(&t_send_segments, NULL, send_segments, (void*)NULL); pthread_create(&t_recv_acks, NULL, recv_acks, (void*)NULL); pthread_join(t_recv_acks, (void**)NULL); pthread_mutex_destroy(&lock_it); pthread_cond_destroy(&write_it); } void* send_segments(void *v) { for(;;) { pthread_mutex_lock(&lock_it); printf('s1\n'); printf('s2\n'); pthread_cond_wait(&write_it, &lock_it); printf('s3\n'); printf('s4\n'); printf('s5\n'); pthread_mutex_unlock(&lock_it); } return 0; } void* recv_acks(void *v) { for(;;) { pthread_mutex_lock(&lock_it); printf('r1\n'); pthread_cond_signal(&write_it); printf('r2\n'); pthread_mutex_unlock(&lock_it); } return 0; }
The expected output is:
s1 s2 r1 s3 s4 s5 s1 s2 r2 r1 s3 s4 s5 (etc)
My output doesn’t follow this pattern at all. Clearly I have a logic error somewhere, but I’m not understanding where. Why doesn’t the recv_acks() thread always yield when it hits the pthread_cond_signal() – since the pthread_cond_wait() always executes first (because of the order in which I create the threads) and the cond_wait() always executes since its in the critical section?
The
pthread_cond_signalfunction does not cause the current thread to yield and does not release the mutex. All it does is restart one thread that has suspended itself on the condition viapthread_cond_wait. This just means the awakened thread is available for scheduling, it doesn’t cause it to execute immediately. The thread-scheduler will schedule it sometime in the future.Also, just because the s-thread has been awakened and is contending for the mutex, that doesn’t mean it’s going to get the mutex next. Mutexes are not necessarily fair to all threads that have requested it. According to the
pthread_mutexman page: ‘pthread_mutex_locklocks the given mutex. If the mutex is currently unlocked, it becomes locked and owned by the calling thread, andpthread_mutex_lockreturns immediately.’ So the r-thread can spin in its loop several times, happily unlocking and relocking the mutex several times before being swapped out by the scheduler. This means the s-thread will only get a chance at the mutex if the scheduler happens to interrupt the r-thread during the brief time in which it has released the mutex.To achieve the output you want, both threads will need to control their execution with a condition and signal each other before suspending themselves. However, this may or may not be what you actually want to do with your real project.
Another note: it doesn’t really matter what order you created the threads in. Creating a thread does not yield the creating thread. So the main thread will probably create both threads before either gets scheduled, and the thread scheduler is free to schedule either one of them for execution next. If the s-thread does run first on your platform, that just happens to be the implementation behavior on your platform and is not something that should be relied on.