pthread_cond_signal导致死锁

Sha*_*oya 3 c linux multithreading deadlock pthreads

我有一个程序在其中一个线程调用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;
}
Run Code Online (Sandbox Code Playgroud)

唯一重要的部分是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
Run Code Online (Sandbox Code Playgroud)

#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
Run Code Online (Sandbox Code Playgroud)

pthread_cond_signal死锁是一个类似的问题.但似乎提问的问题有记忆腐败.我没有内存损坏(说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
Run Code Online (Sandbox Code Playgroud)

主程序是用C++编写的.但我正在使用该语言的C部分,因此避免使用C++标记.

Sha*_*oya 6

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

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