【发布时间】:2011-11-24 10:16:14
【问题描述】:
我有一个程序在其中一个线程调用pthread_cond_siganl(或广播)时死锁。
该问题在主程序中可 100% 重现。我无法弄清楚它有什么问题,因此提取了调用等待和信号的代码。但是,已提取的问题无法重现死锁。
在主程序上运行valgrind 不会报告任何无效读/写或内存泄漏。
我想知道调用pthread_cond_signal时可能出现死锁的原因。
提取出来的sn-p如下。
#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 函数。其他部分只是为了编译。
正如我所说,该问题在此程序中无法重现。 这个sn-p和主程序的区别是:
- 在主程序中,
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++ 标记。
【问题讨论】:
-
(1) 你确定你的程序死锁还是只是在循环运行? (2) 从您发布的代码中,我推断您的原始代码是 C++ 而不是 C?已经使用 C++ 库可能会分散您的注意力。 (3) 吹毛求疵:在这种简单的情况下,不要对
mutex或cond使用init和destroy函数。他们有静态初始化器。 -
添加了一些输出。 @JensGustedt 1. 是的。我确定这是僵局。我提供了线程的死锁回溯。 2. 正确。 3.主程序不简单,我用的是RAII。
-
所以我认为我们无法帮助您处理您提供的信息。您的软件环境似乎比您向我们展示的要复杂得多。您必须将其进一步确定为我们可以重现的情况。
-
@JensGustedt 谢谢。发现了问题。我正在用析构函数以外的方法破坏互斥锁和 condvar :(
标签: c linux multithreading pthreads deadlock