pthread_cond_signal导致死锁

时间:2011-11-23 20:12:09

标签: c linux multithreading pthreads deadlock

我有一个程序在其中一个线程调用pthread_cond_siganl(或广播)时死锁。 该问题在主程序中可100%重现。我无法弄清楚它有什么问题,因此提取了等待和信号调用的代码片段。但是,使用提取的问题无法复制死锁

在主程序上运行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函数。其他部分仅用于编译。

正如我所说,在此程序中无法重现。 这段代码与主程序的区别在于:

  • 在主程序中,mutexcondvar是成员字段,函数是成员方法。
  • 任务完成一些任务而不是睡觉。
  • 多线程可能会等待,我们应该广播而不是信号。但是,即使我使用信号和一个等待线程,死锁也是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_waitpthread_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 个答案:

答案 0 :(得分:6)

愚蠢的错误。 在执行信号和等待之前,我正在摧毁mutexcondvar。 要重现,只需在加入main函数中的线程之前移动destroy函数。

令人惊讶的是,在我的两台机器上,这会产生100%一致(和错误)的行为。

答案 1 :(得分:-1)

当我们调用pthread_cond_wait(&amp; cond,&amp; lock)时,锁将被释放,pthread将等待条件变量。当它获得条件变量上的信号时,它将获得锁定并将从pthread_cond_wait()中获得。在您的程序中,您在调用pthread_cond_broadcast(&amp; cond)之前获得了互斥锁,因此pthread_cond_wait(&amp; cond,&amp; lock)在接收到信号时无法获取锁。我认为这将是造成僵局的原因。