我有一个程序,当其中一个线程调用 pthread_cond_siganl
(或广播)时死锁 . 该问题在主程序中可100%重现 . 我无法弄清楚它有什么问题,因此提取了等待和信号调用的代码片段 . 但是,死锁 cannot 可以用提取的问题进行复制 .
在主程序上运行 valgrind
不会报告任何无效的读/写或内存泄漏 .
我想知道在调用 pthread_cond_signal
时死锁的可能原因是什么 .
提取的片段如下 .
#include <pthread.h>
#include <math.h>
#include <syscall.h>
#include <assert.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
void Task() {
cerr << syscall(SYS_gettid) << " In Task, sleeping..." << endl;
sleep(5);
}
pthread_mutex_t lock;
pthread_cond_t cond;
bool doingTheTask= false;
void* func(void* ) {
pthread_mutex_lock(&lock);
if (doingTheTask) {
cerr << syscall(SYS_gettid) << " wait... " << endl;
while ( doingTheTask) {//spurious wake-up
cerr << syscall(SYS_gettid) << " waiting..." << endl ;
pthread_cond_wait(&cond, &lock);
cerr << syscall(SYS_gettid) << " woke up!!!" << endl ;
}
}
else {
cerr << syscall(SYS_gettid) << " My Turn to do the task..." << endl;
assert( ! doingTheTask );
doingTheTask= true;
pthread_mutex_unlock(&lock);
Task();
cerr << syscall(SYS_gettid) << " Before trying to acquire lock" << endl;
pthread_mutex_lock(&lock);
cerr << syscall(SYS_gettid) << " After acquiring lock" << endl ;
assert( doingTheTask );
doingTheTask = false;
cerr << syscall(SYS_gettid) << " Before broadcast" << endl;
pthread_cond_broadcast(&cond);
cerr << syscall(SYS_gettid) << " After broadcast" << endl;
}
pthread_mutex_unlock(&lock);
return NULL;
}
int main() {
pthread_mutex_init(&lock,NULL);
pthread_cond_init(&cond,NULL);
pthread_t thread[2];
for ( int i = 0 ; i < 2 ; i ++ ) {
if (0 != pthread_create(&thread[i], NULL, func, NULL) ) {
cerr << syscall(SYS_gettid) << " Error creating thread" << endl;
exit(1);
}
}
for ( int i = 0 ; i < 2 ; i ++ ) {
pthread_join(thread[i],NULL);
}
pthread_mutex_destroy(&lock);
pthread_cond_destroy(&cond);
return 0;
}
唯一重要的部分是func函数 . 其他部分仅用于编译 .
正如我所说的问题 is not reproducible in this program . 这段代码与主程序的区别在于:
-
在主程序中,
mutex
和condvar
是成员字段,函数是成员方法 . -
任务完成一些任务而不是睡觉 .
-
多个线程可能会等待,我们应该广播而不是信号 . 但是,即使我使用信号和一个等待线程,死锁也是100%可重现的 .
我试图用这段代码解决的问题是当至少有一个线程需要完成任务时执行一次任务的机制 . 但是没有两个线程可以并行执行任务,一旦其中一个执行任务,其他线程就不需要执行任务 . 该方法的客户端假设它阻塞直到任务完成(因此在看到某人正在执行任务后我无法立即返回) .
死锁线程的回溯是:
#0 __lll_lock_wait () at ../nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:136
#1 0x00007ffff73e291c in pthread_cond_wait@@GLIBC_2.3.2 () at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:259
和
#0 __lll_lock_wait () at ../nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:136
#1 0x00007ffff73e30b1 in pthread_cond_signal@@GLIBC_2.3.2 () at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_signal.S:142
pthread_cond_signal deadlocks是一个类似的问题 . 但似乎提问的问题有记忆腐败 . 我没有内存损坏(说 valgrind
) .
在我测试它的两台机器上,问题是100%可重现的 . (ArchLinux最新和Uubntu 10.04.3) .
下面是主程序的示例输出 . 它再次显示线程在调用 pthread_cond_wait
和 pthread_cond_signal
之前阻塞 . (第一列显示了线程ID) .
3967 In Task, sleeping...
3967 My Turn to do the task...
3967 In Task, sleeping...
3973 wait...
3973 waiting...
3976 <output from some other thread>
3967 Before trying to acquire lock
3967 After acquiring lock
3967 Before broadcast
主程序在C中 . 但我正在使用该语言的C部分,从而避免使用C标签 .
2 回答
愚蠢的错误 . 在执行信号和等待之前,我正在摧毁
mutex
和condvar
. 要重现,只需在连接main函数中的线程之前移动destroy函数 .令人惊讶的是,在我的两台机器上,这会产生100%一致(和错误)的行为 .
当我们调用pthread_cond_wait(&cond,&lock)时,锁将被释放,pthread将等待条件变量 . 当它获得条件变量上的信号时,它将获得锁定并将从pthread_cond_wait()中获得 . 在您的程序中,您在调用pthread_cond_broadcast(&cond)之前获取了互斥锁,因此pthread_cond_wait(&cond,&lock)在接收到信号时无法获取锁 . 我认为这将是僵局的原因 .