当前位置:C++技术网 > 资讯 > Linux下的线程死锁问题:条件变量 pthread_cond_t 用错导致死锁

Linux下的线程死锁问题:条件变量 pthread_cond_t 用错导致死锁

更新时间:2016-08-16 11:56:42浏览次数:1+次

       最近XXXX项目显示后台连上无数据,分析log是发送线程不发送数据包到后台导致,但其它线程已经将包放入队列,但一时难以判断具体原因,觉得很不可思议。根据log,发现其它线程都在运行,唯独发送线程没有运行,初步判断是发送线程死锁。这就缩小了代码排查范围,仔细查看附近的代码,发现对于Linux下用于线程同步的条件变量使用不当,才导致死锁。
    条件变量等待函数原型如下:
int WINPTHREAD_API pthread_cond_wait (pthread_cond_t *cv, pthread_mutex_t *external_mutex)
    第二个参数需要传入一个互斥锁,此锁用于防止竞争。但之前的代码只是为了满足传参需要和编译通过,才定义了一个锁传进去,但在别的地方没有使用这个锁,这样就不能保证多线程操作队列时同时只有1个线程操作,就可能会出现激活条件变量(pthread_cond_signal)在pthread_cond_wait 之前发生,而队列在为空或满时只会激活一次,这就导致pthread_cond_wait 一直等待下去不会被唤醒,从而出现了死锁。
   这种概率是很小的,因为只有碰巧在满足if之后但进入pthread_cond_wait之前发生线程切换,而切换的另一个线程在这个时间片里调用了pthread_cond_signal,在切换回去之前先激活条件,这就出现激活条件早于等待条件,导致死锁。根据这个逻辑,可以在满足队列为空的if之后、pthread_cond_wait之前故意加入长时间的sleep休眠,故意让cpu进行线程切换,无限放大这种死锁的可能性,实际运行时果然出现了预期的死锁,打印信息和现场的log一样。说明这方面是存在问题的,因为如果一个程序没有死锁可能,加入长时间的sleep只会让它运行变慢,但不至于产生死锁。
    理论分析和加入sleep实测都一致表明是这个原因,找到原因后,解决办法就是按照正确标准的方式使用条件变量。pthread_cond_wait中第二个参数代表的锁应该起到对资源访问控制的作用,保证同一时刻只有一个线程操作队列,同时在调用pthread_cond_signal时也必须处于这把锁的保护之下,这样才是正确无误的。
    按照这种道理,发现代码中其它使用条件变量的地方一样有缺陷,因为这些代码都是当时一起写的。同样的,在相应的pthread_cond_wait前加入长时间的sleep,也出现了死锁无法唤醒的情况。解决办法自然是一样的。
    按照上述修改之后,当无论在pthread_cond_wait之前加入多久的sleep时,都不会死锁,最多运行慢一些,说明问题得到解决。
    同时,为了以防万一,把代码中所有的pthread_cond_wait换成了pthread_cond_timedwait定时等待,超时时间五分钟,这样就算死锁五分钟后也会自动解除。不至于永远死锁下去。

    回顾解决过程,虽然这个问题初看似不好解决或让人不知所措,因为很难复现,而代码又看不出原因,其实问题的原因倒很低级,就是基础不扎实、对线程同步理解停留在表面,理解之后再回过头来看当初对条件变量的使用,觉得真的是很低级的错误。