首页 文章

pthread_cond_wait有时不会收到信号

提问于
浏览
2

pthread_cond_waitpthread_cond_signal 我有一个奇怪的问题 . 我已经安排了一系列线程 . 它们在启动时都处于睡眠状态 . 唤醒功能将发出这些线程的信号,做一些工作,并等待结果 .

在下面的设置中, td 是包含互斥锁和条件的线程数据, th 是包含指向线程的指针的数组:

for (size_t i = 0; i < NUM_THREADS; i++) {
    pthread_cond_init(&td[i].cond, NULL);
    pthread_mutex_init(&td[i].cond_mutex, NULL);
    pthread_mutex_init(&td[i].work_mutex, NULL);
    pthread_mutex_lock(&td[i].cond_mutex);
    pthread_mutex_lock(&td[i].work_mutex);
    pthread_create(&th[i], NULL, thread_worker, (void *)&td[i]);
}

线程工作者是这样的:

void*
thread_worker(void* data)
{
    THREAD_DATA *td = (THREAD_DATA *)data;
    while (1) {
        pthread_cond_wait(&td->cond, &td->cond_mutex);  // marker

        // do work ...

        pthread_mutex_unlock(&td->work_mutex);
    }
    pthread_exit(NULL);
}

这个 job 函数应该唤醒所有线程,完成工作,并等待它们完成:

void
job()
{
    for (size_t i = 0; i < NUM_THREADS; i++) {
        pthread_cond_signal(&td[i].cond);
    }
    for (size_t i = 0; i < NUM_THREADS; i++) {
        pthread_mutex_lock(&td[i].work_mutex);  // block until the work is done
    }
}

在一些罕见的情况下(可能是1000次运行中的1次),上述设置将遇到冻结 . 当发生这种情况时, thread_worker 中的'marker'行将不会被 pthread_cond_signal 发出信号,它只是在等待 . 它产生了大量的日志消息,我确认 pthread_cond_wait 总是在 pthread_cond_signal 之前被调用 . 我在这做错了什么?

2 回答

  • 2

    那里没有任何东西迫使 pthread_cond_wait()pthread_cond_signal() 之前被召唤 . 尽管您对日志记录的看法如此,但记录的行完全可能与实际发生的事件无序 .

    您没有正确使用互斥锁和条件变量:互斥锁只能由锁定它们的同一个线程解锁,并且条件变量应该与某个共享状态(称为谓词)的测试配对 . 共享状态应该由传递给 pthread_cond_wait() 的互斥锁保护 .

    例如,您的示例可以重新编写以正确使用互斥锁和条件变量 . 首先,将 int work_status 添加到 THREAD_DATA 结构,其中 0 表示线程正在等待工作, 1 表示工作可用, 2 表示工作已完成 .

    您似乎不需要在每个 THREAD_DATA 中使用两个互斥锁,并且您不需要重新设置它:

    for (size_t i = 0; i < NUM_THREADS; i++) {
        pthread_cond_init(&td[i].cond, NULL);
        pthread_mutex_init(&td[i].cond_mutex, NULL);
        td[i].work_status = 0;
        pthread_create(&th[i], NULL, thread_worker, (void *)&td[i]);
    }
    

    让线程在 work_status 上使用条件变量等待:

    void*
    thread_worker(void* data)
    {
        THREAD_DATA *td = (THREAD_DATA *)data;
    
        while (1) {
            /* Wait for work to be available */
            pthread_mutex_lock(&td->cond_mutex);
            while (td->work_status != 1)
                pthread_cond_wait(&td->cond, &td->cond_mutex);
            pthread_mutex_unlock(&td->cond_mutex);
    
            // do work ...
    
            /* Tell main thread that the work has finished */
            pthread_mutex_lock(&td->cond_mutex);
            td->work_status = 2;
            pthread_cond_signal(&td->cond);
            pthread_mutex_unlock(&td->cond_mutex);
        }
        pthread_exit(NULL);
    }
    

    ...并在 job() 中适当地设置并等待 work_status

    void
    job()
    {
        /* Tell threads that work is available */
        for (size_t i = 0; i < NUM_THREADS; i++) {
            pthread_mutex_lock(&td[i].cond_mutex);
            td[i].work_status = 1;
            pthread_cond_signal(&td[i].cond);
            pthread_mutex_unlock(&td[i].cond_mutex);
        }
    
        /* Wait for threads to signal work complete */
        for (size_t i = 0; i < NUM_THREADS; i++) {
            pthread_mutex_lock(&td[i].cond_mutex);
            while (td[i].work_status != 2)
                pthread_cond_wait(&td[i].cond, &td[i].cond_mutex);
            pthread_mutex_unlock(&td[i].cond_mutex);
        }
    }
    
  • 3

    一些检查清单:

    1)在等待cond变量之前锁定互斥锁 td->cond_mutex 吗?否则,它是未定义的 .

    2)在pthread_cond_wait()返回后检查谓词吗?典型用法是

    while(!flag) pthread_cond_wait(&cv, &mutex); //waits on flag
    

    这不是你拥有的 . 这是为了防止虚假的唤醒,并确保谓词在此期间没有改变 .

    3) pthread_cond_signal() 保证至少唤醒一个线程 . 如果有多个线程在同一条件变量上等待,则可能需要使用 pthread_cond_broadcast() .

    4)如果没有线程在等待条件变量,则 pthread_cond_signal()pthread_cond_broadcast() 无效 .

相关问题