pthread_cond_wait
和 pthread_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 回答
那里没有任何东西迫使
pthread_cond_wait()
在pthread_cond_signal()
之前被召唤 . 尽管您对日志记录的看法如此,但记录的行完全可能与实际发生的事件无序 .您没有正确使用互斥锁和条件变量:互斥锁只能由锁定它们的同一个线程解锁,并且条件变量应该与某个共享状态(称为谓词)的测试配对 . 共享状态应该由传递给
pthread_cond_wait()
的互斥锁保护 .例如,您的示例可以重新编写以正确使用互斥锁和条件变量 . 首先,将
int work_status
添加到THREAD_DATA
结构,其中0
表示线程正在等待工作,1
表示工作可用,2
表示工作已完成 .您似乎不需要在每个
THREAD_DATA
中使用两个互斥锁,并且您不需要重新设置它:让线程在
work_status
上使用条件变量等待:...并在
job()
中适当地设置并等待work_status
:一些检查清单:
1)在等待cond变量之前锁定互斥锁
td->cond_mutex
吗?否则,它是未定义的 .2)在pthread_cond_wait()返回后检查谓词吗?典型用法是
这不是你拥有的 . 这是为了防止虚假的唤醒,并确保谓词在此期间没有改变 .
3)
pthread_cond_signal()
保证至少唤醒一个线程 . 如果有多个线程在同一条件变量上等待,则可能需要使用pthread_cond_broadcast()
.4)如果没有线程在等待条件变量,则
pthread_cond_signal()
或pthread_cond_broadcast()
无效 .