使用条件变量时的意外行为(c,gcc)

时间:2017-06-02 12:18:16

标签: c multithreading concurrency

我正在尝试学习如何在C中正确使用条件变量。 作为一个练习我自己,我试图用2个线程打印一个小程序,打印" Ping"其次是" Pong"在一个无限循环中。

我写了一个小程序:

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

void* T1(){
    printf("thread 1 started\n");

    while(1)
    {
        pthread_mutex_lock(&lock);
        sleep(0.5);
        printf("ping\n");
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&lock);
        pthread_cond_wait(&cond,&lock);
    }
}

void* T2(){
    printf("thread 2 started\n");

    while(1)
    {
        pthread_cond_wait(&cond,&lock);
        pthread_mutex_lock(&lock);
        sleep(0.5);
        printf("pong\n");
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&lock);
    }
}

int main(void)
{
    int i = 1;
    pthread_t t1;
    pthread_t t2;



    printf("main\n");
    pthread_create(&t1,NULL,&T1,NULL);
    pthread_create(&t2,NULL,&T2,NULL);
    while(1){
        sleep(1);
        i++;
    }
    return EXIT_SUCCESS;
}

运行此程序时,我得到的输出是:

main
thread 1 started
thread 2 started
ping

知道程序没有按预期执行的原因是什么?

提前致谢。

2 个答案:

答案 0 :(得分:2)

sleep采用整数,而不是浮点数。不确定sleep(0)在您的系统上做了什么,但这可能是您遇到的问题之一。

您需要在调用pthread_cond_wait时按住互斥锁。

裸条件变量(即条件变量不表示存在读取其他地方的条件)几乎总是错误的。一个条件变量表明我们正在等待的东西可能已经准备好被消费,它们不是用于信令(不是因为它是非法的,而是因为它很难让它们适合纯信令)。所以通常条件如下:

 /* consumer here */
 pthread_mutex_lock(&something_mutex);
 while (something == 0) {
     pthread_cond_wait(&something_cond, &something_mutex);
 }
 consume(something);
 pthread_mutex_unlock(&something_mutex);

 /* ... */

 /* producer here. */
 pthread_mutex_lock(&something_mutex);
 something = 4711;
 pthread_cond_signal(&something_cond, &something_mutex);
 pthread_mutex_unlock(&something_mutex);

在握住锁的同时入睡是一个坏主意。

T1T2不是用作pthread_create函数的有效函数,它们应该接受参数。做得对。

你在cond_signal和cond_wait之间的每个线程中竞争自己,所以每个线程可能只是一直发出信号并不难以置信。 (在pthread_cond_wait的调用中正确地保持互斥体可能对此有所帮助,或者可能没有,这就是为什么我说正确获取裸条件变量很难,因为它是。)

答案 1 :(得分:1)

首先,你永远不应该使用sleep()来同步线程(如果需要降低输出速度,请使用nanosleep())。您可能需要(这是一个常见用途)共享变量ready,让每个线程都知道他可以打印消息。在创建pthread_cond_wait()之前,必须获取锁,因为 pthread_cond_wait()函数将阻塞条件变量。它应该被调用线程锁定的互斥锁或未定义的行为结果调用。

步骤是:

  1. 获取锁
  2. 在guard [*]
  3. 中使用共享变量等待一段时间
  4. 做东西
  5. 更改共享变量的值以进行同步(如果您已经完成)和已完成工作的信号/广播
  6. 解除锁定
  7. 步骤4和5可以颠倒。

    [*] 使用pthread_cond_wait()释放互斥锁并阻塞条件变量上的线程,当使用条件变量时,总会有一个布尔谓词,涉及与每个条件等待关联的共享变量如果线程应该继续,因为可能会发生虚假的唤醒。 watch more here

    pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
    int ready = 0;
    void* T1(){
        printf("thread 1 started\n");
    
        while(1)
        {
            pthread_mutex_lock(&lock);
            while(ready == 1){
                pthread_cond_wait(&cond,&lock);
            }
            printf("ping\n");
            ready = 1;
            pthread_cond_signal(&cond);
            pthread_mutex_unlock(&lock);
        }
    }
    
    void* T2(){
        printf("thread 2 started\n");
    
        while(1)
        {
            pthread_mutex_lock(&lock);
            while(ready == 0){
                pthread_cond_wait(&cond,&lock);
            }
            printf("pong\n");
            ready = 0;
            pthread_cond_signal(&cond);
            pthread_mutex_unlock(&lock);
        }
    }
    
    int main(void)
    {
        int i = 1;
        pthread_t t1;
        pthread_t t2;
    
    
    
        printf("main\n");
        pthread_create(&t1,NULL,&T1,NULL);
        pthread_create(&t2,NULL,&T2,NULL);
        pthread_join(t1,NULL);
        pthread_join(t2,NULL);
        return EXIT_SUCCESS;
    }
    

    您还应该使用pthread_join()中的main代替while(1)