【问题标题】:pthread_cond_signal causing deadlockpthread_cond_signal 导致死锁
【发布时间】: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和主程序的区别是:

  • 在主程序中,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++ 标记。

【问题讨论】:

  • (1) 你确定你的程序死锁还是只是在循环运行? (2) 从您发布的代码中,我推断您的原始代码是 C++ 而不是 C?已经使用 C++ 库可能会分散您的注意力。 (3) 吹毛求疵:在这种简单的情况下,不要对mutexcond 使用initdestroy 函数。他们有静态初始化器。
  • 添加了一些输出。 @JensGustedt 1. 是的。我确定这是僵局。我提供了线程的死锁回溯。 2. 正确。 3.主程序不简单,我用的是RAII。
  • 所以我认为我们无法帮助您处理您提供的信息。您的软件环境似乎比您向我们展示的要复杂得多。您必须将其进一步确定为我们可以重现的情况。
  • @JensGustedt 谢谢。发现了问题。我正在用析构函数以外的方法破坏互斥锁和 condvar :(

标签: c linux multithreading pthreads deadlock


【解决方案1】:

愚蠢的错误。 在执行信号并等待之前,我正在破坏mutexcondvar。 要重现,只需在加入主函数中的线程之前移动销毁函数。

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

【讨论】:

  • 我不明白为什么这会让你感到惊讶。在被破坏的结构上调用任何函数(但init)是未定义的行为,不是吗?我想说,任何事情都有可能发生。
【解决方案2】:

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

【讨论】:

  • 我不这么认为。来自:computing.llnl.gov/tutorials/pthreads/man/…“如果需要可预测的调度行为,则该互斥锁应由调用 pthread_cond_broadcast() 或 pthread_cond_signal() 的线程锁定。”在 SO 中也多次讨论过在发出信号之前锁定或不锁定互斥锁。简而言之:“应该被锁定”
猜你喜欢
  • 2015-03-09
  • 1970-01-01
  • 2018-04-20
  • 2012-09-20
  • 2018-06-04
  • 2016-12-22
  • 1970-01-01
  • 1970-01-01
  • 2015-08-18
相关资源
最近更新 更多