Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

POSIX C Threads. pthread_cond_t example. Doesn't work as expected

I wrote a wrote a program and it doesn't work as I expect it to. I have two threads: thread triggers func and anotherThread triggers anotherFunc. What I wanted to do is when cont reaches value 10 in func, anotherThread to be triggered using pthread_cond_wait and pthread_cond_signal. The strange thing is everything works fine if i uncomment the sleep(1) line. I'm new to threads and I was following the tutorial here and if I comment the sleep line in their example it breaks as well.

My question is how can I make this work without any sleep() calls? And what happens if in my code both func reaches pthread_mutex_lock after anotherFunc? How can I control these things? This is my code:

#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>

pthread_mutex_t myMutex;
pthread_cond_t cond;
pthread_attr_t attr;

int cont;

void *func(void*)
{
    printf("func\n");

    for(int i = 0; i < 20; i++)
    {
        pthread_mutex_lock(&myMutex);

        cont++;
        printf("%d\n", cont);
        if(cont == 10)
        {
            printf("signal:\n");
            pthread_cond_signal(&cond);
//            sleep(1);
        }
        pthread_mutex_unlock(&myMutex);
    }
    printf("Done func\n");

    pthread_exit(NULL);
}

void *anotherFunc(void*)
{
    printf("anotherFunc\n");
    pthread_mutex_lock(&myMutex);
    printf("waiting...\n");

    pthread_cond_wait(&cond, &myMutex);
    cont += 10;
    printf("slot\n");

    pthread_mutex_unlock(&myMutex);
    printf("mutex unlocked anotherFunc\n");
    printf("Done anotherFunc\n");

    pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
    pthread_t thread;
    pthread_t anotherThread;

    pthread_attr_init(&attr);
    pthread_mutex_init(&myMutex, NULL);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    pthread_cond_init(&cond, NULL);

    pthread_create(&anotherThread, &attr, anotherFunc, NULL);
    pthread_create(&thread, &attr, func, NULL);

    pthread_join(thread, NULL);
    pthread_join(anotherThread, NULL);

    printf("Done MAIN()");

    pthread_mutex_destroy(&myMutex);
    pthread_cond_destroy(&cond);
    pthread_attr_destroy(&attr);


    pthread_exit(NULL);
    return 0;
}

Sorry for the long post but I'm new to threads and I'm willing to learn. Also do you know some good references or courses/tutorials on threads and networking on Linux? I want to learn create an chat client and I heard that I have to know threads and networking for that. Problem is I don't know pretty good if what I learn is ok since I don't know what I have to know.

Thank you so much :)

like image 469
Jacob Krieg Avatar asked Apr 16 '12 18:04

Jacob Krieg


2 Answers

Your anotherThread simply calls pthread_cond_wait without first testing whether the desired condition (counter reaching ten) has already happened. This is incorrect logic, which will lead to the lost wakeup problem: the name of a recurring bug that plagues incorrectly written multithreaded programs.

Condition variables are stateless. If pthread_cond_signal or pthread_cond_broadcast is called on a condition on which no threads are currently waiting, the operation has no effect. It is not remembered. So it is possible for your signaling thread to count to 10 very quickly, and signal the condition variable, before the other thread has reached the pthread_cond_wait call.

You need a loop around pthread_cond_wait. The condition must be checked in case it is already true, so that the thread does not wait for a wakeup which already happened. And it should be a loop because wakeups can be spurious: just because the thread falls through the pthread_cond_wait doesn't mean that the condition is actually true:

while (cont < 10)
  pthread_cond_wait(&cond, &myMutex);

Also, there is no need to create a thread attribute just to make a thread joinable. This is the default situation when you use a null pointer for the creation attribute. POSIX threads are joinable unless created detached, or converted to detached with pthread_detach.

Another thing: whenever possible, avoid calling pthread_cond_signal while holding a mutex lock. It's not incorrect, but it's potentially wasteful, because the operation may actually have to call into the OS kernel to wake up a thread, and so you're holding this expensive mutex lock over the entire system call (when all you really need it for is protecting a few machine instructions that working with shared data in your application).

like image 182
Kaz Avatar answered Sep 30 '22 17:09

Kaz


I don't know what your actual problem is (what happens when it doesn't work?)..

I see one major problem, you don't handle the spurious wakeups.

You need something that signals that the condition really true, for example with a boolean variable:

init:

signaled = false;

signal:

signaled = true;
pthread_cond_signal(&cond);

receive:

while (!signaled)
   pthread_cond_wait(&cond, &myMutex);
like image 39
Karoly Horvath Avatar answered Sep 30 '22 16:09

Karoly Horvath