代码之家  ›  专栏  ›  技术社区  ›  Eunjeong Choi

为什么此代码返回意外结果?(条件变量)

  •  2
  • Eunjeong Choi  · 技术社区  · 7 年前

    我正在研究互斥锁。

    所以,我编写了关于互斥锁的测试代码。

    但这段代码有一个问题。

    我想展示:

    downloading.....
    complete....
    start play!
    

    但在运行时,结果是:

    downloading.....
    complete....
    

    我不知道 1、为什么此代码有错误? 2、如何修复此代码?

    #include <stdio.h>
    #include <pthread.h>
    #include <unistd.h>
    
    
    int buffer = 0;
    
    pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
    
    void* download(void *arg){
        printf("downloading.....\n");
        pthread_mutex_lock(&lock);
        sleep(3);
        buffer = 10;
        printf("complete....\n");
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&lock);
        return NULL;
    }
    
    void* play(void *arg){
    
        pthread_mutex_lock(&lock);
        pthread_cond_wait(&cond, &lock);
        printf("start play!\n");
        --buffer;
    
        pthread_mutex_unlock(&lock);
        return NULL;
    }
    
    int main(){
        pthread_t tid1, tid2;
    
        pthread_create(&tid1, NULL, play, NULL);
    
        pthread_create(&tid2, NULL, download, NULL);
    
        pthread_join(tid1, NULL);
    
        pthread_join(tid2, NULL);
    }
    
    1 回复  |  直到 7 年前
        1
  •  0
  •   Michael Burr    7 年前

    这个 download() 函数碰巧首先运行,获取互斥锁并完成其所有工作(包括调用 pthread-cond_signal() 释放互斥锁之前。

    这个 play() 线程没有机会调用 pthread_cond_wait() 在发送信号之前,因为 下载() 碰巧先获得互斥锁。那么什么时候 播放() 得到一个等待的机会,它已经错过了唤醒它的信号。

    修复方法是将条件变量与标志(可能)结合使用 buffer -取决于您实际想要做什么)。需要对标志执行以下操作:

    • 调用前必须检查标志 pthread\u cond\u wait() ,如果标志指示条件已满足,则应等待 完成
    • “仅当条件尚未满足时才检查标志/等待”操作应在循环中执行,因为 pthread\u cond\u wait() 可能会错误返回(即未发出信号)
    • 标志检查 对标志的任何更新(无论在哪个线程中)都必须在保持互斥锁的同时进行

    类似于(以下代码未经测试):

    void* play(void *arg)
    {
        pthread_mutex_lock(&lock);
        while (buffer < 1) {
            pthread_cond_wait(&cond, &lock);
        }
        printf("start play!\n");
        --buffer;
    
        pthread_mutex_unlock(&lock);
        return NULL;
    }