1

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

  • https://stackoverflow.com/q/14947191/2864740 - pthread_mutex_lock is not guaranteed to be fair, and the ordering of the create also doesn't guarantee scheduling. Anyway, sleep for a second after the unlock in f1 to get different behavior (still not proper, yet it should show the reason..). – user2864740 Dec 08 '21 at 07:15
  • @user2864740 - adding a sleep statements to make multi-threaded code have expected behavior is a terrible pattern to evangelize. It will mostly work, but on a complicated system, it's an incorrect assumption that one thread "waiting long enough" guarantees the other thread has finished its work. It defeats the whole point of condition vars. It will lead to strange bugs. – selbie Dec 08 '21 at 07:19
  • 1
    @user2864740 - thanks for clarifying. – selbie Dec 08 '21 at 07:21

1 Answers1

1

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

selbie
  • 100,020
  • 15
  • 103
  • 173
  • thanks @selbie, "IncrementX: x+=val, f2: WaitForX(40, 0), f1: WaitForX(40, 1)", that works perfectly, but my main question is how to manage a signal before context switching, without using sleep or using "WaitForX" in f1. I want f1 to wait after X changes and then second thread check X value and after that f1 continue and this happen again and again, maybe I want to use more than one thread, and I want use pthread_cond_broadcast, I dont want to define a specified point in f1 to wait, I want f1 to wait after changing X, – Behrang Shirdel Dec 08 '21 at 07:42
  • 1
    I used: pthread_exit(NULL); at main function, so I didn't use join. – Behrang Shirdel Dec 08 '21 at 07:50