关于多线程:什么是死锁?

What is a deadlock?

编写多线程应用程序时,遇到的最常见问题之一是死锁。

我对社区的问题是:

  • 什么是僵局?

  • 您如何检测到它们?

  • 你会处理吗?

  • 最后,您如何防止它们发生?


  • 当多个进程尝试同时访问同一资源时,将发生锁定。

    一个过程失败了,必须等待另一个过程完成。

    当等待进程仍保留第一个需要完成的另一个资源时,就会发生死锁。

    举个例子:

    资源A和资源B由流程X和Y使用

    • X开始使用A。
    • X和Y尝试开始使用B
    • Y"赢"并首先获得B
    • 现在Y需要使用A
    • A被X锁定,正在等待Y

    避免死锁的最佳方法是避免进程以这种方式交叉。尽可能减少锁定任何东西的需要。

    在数据库中,请避免在单个事务中对不同表进行大量更改,避免使用触发器,并尽可能切换到乐观/脏/无锁读取。


    让我解释一下犯罪电影中出现僵局的真实示例(不是真实示例)。想象一下,一个罪犯将人质扣为人质,而警察也将人质作为罪犯的朋友。在这种情况下,如果警察不让他的朋友放手,罪犯就不会放开人质。另外,除非罪犯释放人质,否则警察不会放任罪犯的朋友。这是一种无休止的不信任局面,因为双方都在坚持彼此的第一步。

    犯罪与警察现场

    enter image description here

    简而言之,当两个线程需要两个不同的资源,并且每个线程都拥有另一个需要的资源锁时,这就是一个死锁。

    僵局的另一个高级解释:破碎的心

    您正在与一个女孩约会,吵架后的第二天,双方彼此伤心欲绝,正等着我对不起和我想念您的电话。在这种情况下,当且仅当其中一方收到对方的I-am-sorry呼叫时,双方才希望彼此通信。因为每个都不打算开始通信并在被动状态下等待,所以两个都将等待另一个开始通信,最终陷入僵局。


    只有当您同时拥有两个或两个以上的锁并且以不同的顺序抓住它们时,才会发生死锁。

    避免出现死锁的方法是:

    • 避免有锁(如果可能),
    • 避免有多个锁
    • 始终以相同的顺序锁。

    要定义死锁,首先我要定义过程。

    进程:众所周知,进程只是执行中的program

    资源:执行程序过程需要一些资源。资源类别可能包括内存,打印机,CPU,打开的文件,磁带驱动器,CD-ROM等。

    死锁:当两个或多个进程持有一些资源并试图获取更多资源,并且直到完成执行后才能释放资源,这是一种情况或状况。

    死锁情况或情况

    enter image description here

    在上图中,有两个过程P1和p2,有两个资源R1和R2。

    资源R1被分配给进程P1,资源R2被分配给进程p2。
    为了完成过程P1的执行,需要资源R2,因此P1请求R2,但是R2已分配给P2。

    以相同的方式,进程P2完成其执行需要R1,但R1已分配给P1。

    这两个进程只有在完成执行后才能释放资源。因此,两者都在等待其他资源,他们将永远等待。所以这是一个死锁条件。

    为了使死锁发生,必须满足四个条件。

  • 互斥-每种资源当前要么正好分配给一个进程,要么可用。 (两个过程不能
    同时控制同一资源或处于关键地位
    部分)。
  • 保留和等待-当前保留资源的进程可以请求新资源。
  • 无抢占-进程拥有资源后,其他进程或内核将无法抢占资源。
  • 循环等待-每个进程都在等待获取由另一个进程持有的资源。
  • 以上条件均满足。


    当线程正在等待永远不会发生的事情时,就会发生死锁。

    通常,它发生在线程正在等待以前所有者从未释放过的互斥量或信号量时。

    当您遇到涉及两个线程和两个锁的情况时,也经常发生这种情况:

    1
    2
    3
    4
    Thread 1               Thread 2

    Lock1->Lock();         Lock2->Lock();
    WaitForLock2();        WaitForLock1();   <-- Oops!

    通常,您检测到它们是因为您期望发生的事情永远不会发生,或者应用程序完全挂起。


    死锁是OS中多处理/多编程问题中的常见问题。
    假设有两个进程P1,P2和两个全局可共享资源R1,R2,并且在关键部分都需要访问这两个资源

    最初,OS将R1分配给进程P1,将R2分配给进程P2。
    当两个进程同时运行时,它们可能会开始执行其代码,但是当一个进程到达关键部分时,就会出现问题。
    因此,进程R1将等待进程P2释放R2,反之亦然...
    因此,他们将永远等待(死锁状态)。

    一个小类比...

    Your Mother(OS),
    You(P1),
    Your brother(P2),
    Apple(R1),
    Knife(R2),
    critical section(cutting apple with knife).

    Your mother gives you the apple and the knife to your brother in the beginning.
    Both are happy and playing(Executing their codes).
    Anyone of you wants to cut the apple(critical section) at some point.
    You don't want to give the apple to your brother.
    Your brother doesn't want to give the knife to you.
    So both of you are going to wait for a long very long time :)


    您可以在"死锁"部分中查看这些精彩的文章。它是用C#语言编写的,但对于其他平台来说,想法仍然相同。我在这里引用以方便阅读

    A deadlock happens when two threads each wait for a resource held by
    the other, so neither can proceed. The easiest way to illustrate this
    is with two locks:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    object locker1 = new object();
    object locker2 = new object();

    new Thread (() => {
                        lock (locker1)
                        {
                          Thread.Sleep (1000);
                          lock (locker2);      // Deadlock
                        }
                      }).Start();
    lock (locker2)
    {
      Thread.Sleep (1000);
      lock (locker1);                          // Deadlock
    }

    当有一个循环的线程或进程链,每个线程或进程都拥有一个锁定的资源,并试图锁定该链中下一个元素所拥有的资源时,就会发生死锁。例如,两个分别持有锁A和锁B的线程都试图获取另一个锁。


    当两个线程获得锁而阻止它们中的任何一个前进时,就会发生死锁。避免它们的最好方法是精心开发。许多嵌入式系统通过使用看门狗计时器(该计时器在系统挂起一定时间后会重置系统的计时器)来保护它们。


    一个经典且非常简单的程序,用于了解死锁情况:-

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public class Lazy {

        private static boolean initialized = false;

        static {
            Thread t = new Thread(new Runnable() {
                public void run() {
                    initialized = true;
                }
            });

            t.start();

            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        public static void main(String[] args) {
            System.out.println(initialized);
        }
    }

    当主线程调用Lazy.main时,它将检查类Lazy
    已经初始化并开始初始化该类。的
    主线程现在将初始化设置为false,创建并启动背景
    其运行方法设置为true的线程,并等待后台线程完成。

    这次,该类当前正在由另一个线程初始化。
    在这种情况下,当前线程(即后台线程)
    等待Class对象,直到初始化完成。不幸的是,线程
    正在执行初始化,主线程正在等待后台
    线程完成。由于两个线程现在正在互相等待,因此
    程序已死锁。


    死锁是系统的一种状态,其中没有单个进程/线程能够执行操作。正如其他人所提到的,死锁通常是以下情况的结果:每个进程/线程希望获得对已由另一个(甚至相同)进程/线程锁定的资源的锁定。

    有多种方法可以找到它们并避免它们。一个人非常努力地思考和/或尝试了许多事情。但是,处理并行性非常困难,并且大多数(如果不是全部)人将无法完全避免问题。

    如果您认真对待此类问题,则可以使用一些更正式的方法。我知道的最实用的方法是使用过程理论方法。在这里,您可以使用某种过程语言(例如CCS,CSP,ACP,mCRL2,LOTOS)对系统进行建模,并使用可用工具对死锁(以及可能还有其他一些属性)进行(建模)检查。使用的工具集示例包括FDR,mCRL2,CADP和Uppaal。一些勇敢的灵魂甚至可以通过使用纯粹的象征性方法来证明他们的系统没有死锁(定理证明;寻找Owicki-Gries)。

    但是,这些形式化方法通常确实需要付出一些努力(例如,学习过程理论的基础知识)。但是我想这仅仅是这些问题很难解决的结果。


    当不同进程请求的可用资源数量较少时,就会发生死锁。这意味着当可用资源的数量少于用户请求的数量时,该过程将进入等待状态。有时等待时间会增加,因此没有机会检查资源不足的问题。这种情况称为死锁。
    实际上,死锁对我们来说是一个主要问题,死锁仅在多任务操作系统中发生。死锁在单任务操作系统中无法发生,因为所有资源仅存在于当前正在运行的任务中......


    尽管这是最常见的原因,但死锁不仅仅与锁一起发生。在C ++中,可以通过让每个线程在另一个对std :: thread对象上调用join()来创建具有两个线程且没有锁的死锁。


    以上是一些很好的解释。希望这也可能有用:
    https://ora-data.blogspot.in/2017/04/deadlock-in-oracle.html

    在数据库中,当一个会话(例如ora)想要另一个会话持有的资源(例如数据),但是该会话(数据)也想要一个第一个会话(ora)持有的资源时。也可以涉及两个以上的会话,但想法是相同的。
    实际上,死锁会阻止某些事务继续工作。
    例如:
    假设,ORA-DATA持有锁A并请求锁B
    SKU持有锁B并请求锁A。

    谢谢,


    当一个线程正在等待其他线程完成时发生死锁,反之亦然。

    如何避免?
    -避免嵌套锁
    -避免不必要的锁
    -使用线程join()

    您如何检测到它?
    在cmd中运行以下命令:

    1
    jcmd $PID Thread.print

    参考:geeksforgeeks


    互斥锁本质上是一个锁,它提供对共享资源的受保护的访问。在Linux下,线程互斥量数据类型为pthread_mutex_t。使用之前,请对其进行初始化。

    要访问共享资源,您必须锁定互斥锁。如果互斥锁已处于锁定状态,则调用将阻塞线程,直到互斥锁解锁为止。访问共享资源完成后,您必须将其解锁。

    总体而言,有一些未成文的基本原则:

    • 使用共享资源之前,请获取锁。

    • 尽可能短地握住锁。

    • 如果线程返回错误,则释放锁。


    推荐阅读

      linux端口进程命令?

      linux端口进程命令?,系统,情况,地址,网络,信息,状态,灵活,工具,端口,命令,如

      linux进程的命令行?

      linux进程的命令行?,地址,工作,系统,信息,命令,管理,名称,进程,目录,服务,lin

      linux当前进程命令?

      linux当前进程命令?,系统,信息,工作,状态,命令,进程,情况,地址,软件,实时,lin

      linux查看多线程命令?

      linux查看多线程命令?,系统,第一,线程,地址,数据,进程,命令,名称,软件,情况,

      linux下杀进程命令?

      linux下杀进程命令?,系统,管理,进程,命令,名称,代码,终端,结束,指令,信号,Lin

      linux关于的进程命令?

      linux关于的进程命令?,系统,进程,管理,命令,名称,代码,软件,信息,定期,状态,l

      linux终止进程命令键?

      linux终止进程命令键?,系统,管理,进程,软件,暂停,工具,命令,代码,名称,传播,l

      linux向进程发送命令?

      linux向进程发送命令?,通信,地址,系统,时间,工作,信息,管理,状态,进程,数据,l

      linux重启进程号命令?

      linux重启进程号命令?,工作,系统,地址,标准,命令,设备,工具,信息,基础,情况,L

      linux中调度进程命令?

      linux中调度进程命令?,系统,状态,策略,实时,信息,进程,数据,管理,时间,异常,L

      linux检测进程命令?

      linux检测进程命令?,系统,服务,地址,状态,信息,检测,进程,命令,第一,软件,lin

      linux等待3秒命令?

      linux等待3秒命令?,时间,地址,工作,暂停,系统,环境,命令,状态,信息,代理,linu

      linux进程中断命令行?

      linux进程中断命令行?,系统,软件,管理,进程,信息,名字,名称,平台,命令,结束,l

      linux进程有关命令?

      linux进程有关命令?,系统,状态,信息,时间,进程,命令,百分比,暂停,名称,定期,L

      linux进程管理命令?

      linux进程管理命令?,系统,管理,地址,工作,状态,进程,信息,时间,基础,命令,Lin

      linux命令进程占系统?

      linux命令进程占系统?,系统,情况,时间,实时,基础,进程,命令,分析,信息,衍生,L

      linux下载命令多线程?

      linux下载命令多线程?,系统,单位,概念,数据,线程,进程,产品,地址,代码,状态,

      linux追踪进程命令?

      linux追踪进程命令?,系统,进程,情况,状态,命令,软件,信息,程序,终端,参数,哪

      终止进程的命令linux?

      终止进程的命令linux?,系统,名称,进程,软件,名字,信息,平台,代码,命令,方式,L

      查询进程命令linux?

      查询进程命令linux?,系统,进程,名称,情况,信息,命令,状态,软件,工具,第一,哪