Jacob Krieg
Jacob Krieg

Reputation: 3184

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. I want to learn to create a chat client and I heard that I have to know threads and networking for that. Problem is I don't even know that what I learn is ok - since I don't know what I have to know.

Thank you so much :)

Upvotes: 7

Views: 15270

Answers (4)

Loki Astari
Loki Astari

Reputation: 264719

What you want is a semaphore not a condition variable.

A semaphore maintains state and counts wait() and signals() against it.
It is usually implemented using a condition variable.

Look here for a trivial implementation.

Upvotes: 1

Erwald
Erwald

Reputation: 2216

This is just an idea but you could also use semaphores to do the "trigger". I remember a project I work on a while ago and what we did is while one of our thread was waiting to be triggered (using a sem_wait) it would simply wait for an infinite time trying to get the lock (and thus was been taken off the proc by the scheduler and saving precious cycles). Once the main thread was done doing his calculation, it would release the semaphore allowing the second thread to do it's calculation.

This is simply a different alternative.

Upvotes: 0

Kaz
Kaz

Reputation: 58657

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).

Upvotes: 10

Karoly Horvath
Karoly Horvath

Reputation: 96326

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);

Upvotes: 2

Related Questions