当前位置:C++技术网 > 资讯 > 编程中如何检测多进程(线程)死锁状态,一招教你搞定死锁检测

编程中如何检测多进程(线程)死锁状态,一招教你搞定死锁检测

更新时间:2017-05-31 15:44:23浏览次数:1+次

    在代码中如果在多个线程之间访问一个全局变量,那么全局变量就构成了临界资源。所谓临界资源,就是共享资源,比如共享单车。对于临界资源的使用,我们需要遵守一定的规则,才能确保共享资源的有序使用。
    因为多个线程是没有运行先后顺序的。系统在调度线程执行的时候,根据内部的调度规则进行调度,让谁执行谁就执行,让谁停止谁就停止。这个规则对于我们程序员或者用户来讲,都是不可见的。在我们看来,里面的调度规则我们毫不知情,也不必知情。我们只需要知道,线程的调度,是无法预测的。所以我们在写多线程程序时,不要期望哪个线程先执行哪个线程后执行。也不要期望线程能一次性全部执行完毕,因为线程随时都可能被系统拉出CPU,放到内存里等待再次调度进CPU。
    因此,多个线程在访问临界资源时,访问的开始时间、持续时间、结束时间都是无法预测的。多个线程之间,谁此时在执行也无法知晓。所以,在对临界资源的共享使用时,如果要确保正确使用,我们需要进行一些保护。因为同时对全局变量赋值,因为多个线程先后不确定,你也不知道最终全局变量得到了什么值。进而得知,此时读取出来的值你也无法确定。这样也就让结果会很怪异。读取的值一会是这个,一会是那个。
    所以,我们使用锁机制来做一个保护。当一个线程在使用临界资源前,先看看资源有么有被加锁,如果被锁住了,那么就等待资源被解锁后再使用,没有被加锁,就直接使用,并且加锁资源,防止他人使用。提示一下,多进程和多线程差不多,就不单独描述多进程了。
    使用的流程如下:
1.资源是否被其他人锁住。
2.如果锁住,等待解锁。
3.如果没有锁住,使用资源,锁住资源。
4.用完解锁释放资源。
    这个基本的逻辑相信大家也都知道。但是还是可能出现问题,那就是死锁的问题。死锁发生在你锁住了资源不释放资源就退出了。这样他人无法使用资源,而你也不再释放资源,他人一直死等这个资源的释放。症状就是,CPU使用率为0,线程一直处于等待阻塞状态,时间长之后,甚至会被系统挂起来。
    我的代码中出现这个死锁问题,原因在于在函数开头访问共享资源时锁定,然后函数结尾时解锁。后续再修改代码时,在中途直接返回了,而没有解锁。而这个问题只有在中途触发了直接返回的代码时才表现出来,很容易让人蒙圈。
    好在我们可以通过CPU使用率、程序执行情况加以判断。我判断的依据就是,线程的CPU使用率为0%,程序不能正常响应请求。如果是死循环,CPU会形成很高的使用率,同时导致不能正常响应请求。
    那么死锁如何检测呢?有没有官方或者高大上的检测工具呢?以前听说过有死锁检测工具,不过查了一下之后,发现有点麻烦。但是又要解决死锁检测问题,怎么办呢?
    然后突然想到了解决办法,那就是打日志。打日志是一种很常见的调试跟踪手段,可以加以利用,比那些检测工具好用多了,简单明了。思路就是,在请求锁之前,打印一个日志,访问资源时打一个日志,解锁退出之后打一个日志。如果一个地方连续出现三条日志,没有问题。如果只出现第一条,那么这一处就一直处于等待状态。如果一处出现两条,那么就是中场退出,没有解锁。这样也就造成了死锁。
    通过日志文件,我们就可以一目了然的看到锁的使用状态,从而可以迅速定位死锁的位置。下面是我实际检测到死锁的日志文件截图:
    编程中如何检测多进程(线程)死锁状态,一招教你搞定死锁检测
    可以看到,前面两个箭头指出来,进入了锁定状态,后续并没有解锁。而底下的箭头表示,此线程在进入锁定前被阻塞了,一直处于等待状态。这样也就形成了死锁。我在日志里将打印日志的函数名都打印出来了,这样可以分分钟定位到死锁的位置,从而解决问题。
    写这篇经验的目的在于,分享一个简单的检测死锁的方法,让我们知道,不要迷信权威的检测办法。因为我一开始就从权威的检测工具上面去思考检测死锁问题了,然后无果,也懒得继续查,就用日志的方式解决了。