pthread_cond_wait有时不会收到信号

时间:2015-07-13 14:49:55

标签: c++ c multithreading pthreads posix

我对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中的“标记”行不会被pthread_cond_signal发出信号,只是等待。这是非常罕见的,但它不时发生。我已经生成了大量日志消息,并且我确认pthread_cond_wait始终在pthread_cond_signal之前调用。我在这里做错了什么?

2 个答案:

答案 0 :(得分:3)

没有任何内容强制pthread_cond_wait()pthread_cond_signal()之前被调用。尽管您对日志记录的看法如此,但记录的行完全可能与实际发生的事件不一致。

您没有正确使用互斥锁和条件变量:互斥锁只能由锁定它们的同一个线程解锁,条件变量应该与某个共享状态的测试配对(称为谓词< / em>的)。共享状态应该被传递给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);
}

...并在work_status

中根据需要设置并等待job()
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);
    }
}

答案 1 :(得分:2)

一些检查清单:

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()无效。