Search code examples
cmultithreadingpthreadsmutexcondition-variable

pthread_cond_wait donst wake-up when using loop


I'm new at threading I want to use ptherad_cond_signal & pthread_cond_wait to check some condition I have this code as a sample:

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int x = 0;
void* f1(void *arg){

    for (int i = 0; i < 10; i++)
    {
        pthread_mutex_lock(&lock);
        x += 10;
        printf("%d\n", x);
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&lock);
    }
    return NULL;
}


void* f2(void* arg){
    pthread_mutex_lock(&lock);
    while (x < 40)
    {
        pthread_cond_wait(&cond, &lock);
    }

    x -= 40;
    pthread_mutex_unlock(&lock);
    return NULL;
}


int main(int argc, char *args[]){    
    pthread_t p1, p2;

    pthread_create(&p2, NULL, f2, NULL);
    pthread_create(&p1, NULL, f1, NULL);    
    
    pthread_exit(NULL);
    
    return 0;
}

result:

10
20
30
40
50
60
70
80
90
100

but I expect:

10
20
30
40
10
20
30
40
50
60

why after pthread_cond_signal, function f2 doesn't continue?
It seems in f1 for loop, locks again before pthread_cond_wait wakeing up


Solution

  • Your first issue is that main creates two threads, but will likely exit (and exit your program) before the other two threads complete. Maybe there's a nuance of threads on a Linux build waits for all threads to complete before exiting a process when main returns. I always forget the rules here, but this is my preferred pattern for main to wait for the child threads to complete. It's a simple change

    int main(int argc, char *args[]){    
        pthread_t p1, p2;
    
        pthread_create(&p2, NULL, f2, NULL);
        pthread_create(&p1, NULL, f1, NULL);    
        
        // wait for both threads to complete
        pthread_join(&p1, NULL);
        pthread_join(&p2, NULL);
        
        return 0;
    }
    

    Back to your original issue.

    There's nothing that blocks f1 from incrementing to 100 before a context switch to f2.

    It sounds like what you want is for f1 to increment X to 40, then wait for f2 to drop it back down before continuing the increment interval up to 60 again.

    You can easily use your condition variable in both threads to be notified of changes and to wait on the other thread.

    I've refactored some of your code so I didn't have to re-write the same block over and over. But it's still mostly your original code.

    void IncrementX(int val)
    {
        pthread_mutex_lock(&lock);
        x += 10;
        printf("%d\n", x);
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&lock);
    
    }
    
    void WaitForX(int target, int gt)
    {
        pthread_mutex_lock(&lock);
        while ((x >= target && gt) || (x < target && !gt))
        {
            pthread_cond_wait(&cond, &lock);
        }
        pthread_mutex_unlock(&lock);    
    }
    
    
    void* f1(void *arg){
    
        // increment to 40
        for (int i = 0; i < 4; i++)
        {
            IncrementX(10);
        }
    
        WaitForX(40, 0); // wait for X to drop below 40
    
        // increment back to 60
        for (int i = 0; i < 6; i++)
        {
            IncrementX(10);
        }
    
        return NULL;
    }
    
    void* f2(void* arg){
    
        WaitForX(40, 1); // wait for X to go to 40 or above
    
        IncrementX(-40);
    
        return NULL;
    }