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