昆山网站建设巴中网站建设

优博企业信息网 2026/09/09 18:35:48

在 C# / .NET 里,死锁的“四个必要条件”其实很好理解,把它们看成导致“互相卡住”的四块拼图——四块都存在时,才会真的卡死。只要你在设计里打碎其中一块,就能避免死锁。
下面逐个讲,每个条件都配一段通俗解释和 C# 示例。

  1. 互斥: 某个资源在同一时刻只能被一个线程使用(如 lock 锁定的对象)
  2. 持有并等待: 线程已经拿着一个资源,还不放手;同时又在等待另一个资源。
  3. 不可抢占:线程持有的资源不能被强制剥夺,只能主动释放。
  4. 循环等待: 线程 A 等待线程 B 的资源,线程 B 等待线程 A 的资源,形成闭环
  5. 互斥条件(Mutual Exclusion)
    1.1 概念(白话版)
    互斥意思是:某个资源在同一时刻只能被一个线程使用。
    类比:
  • 一间厕所只有一个坑位(资源);
  • A 进去上厕所时,门锁上,B 只能在外等;
  • 厕所就是“互斥资源”。

在 C# 中,常见的互斥资源包括:

  • 被 lock 保护的临界区;
  • Mutex、Semaphore(1,1) 等;
  • 文件句柄、数据库连接等有限资源。
private readonly object _lockObj = new object(); private int _counter = 0; public void Increase() { lock (_lockObj) // 这里就是互斥 { // 同一时刻,只能有一个线程在这段代码里 _counter++; Thread.Sleep(100); // 模拟一些工作 } }
  • lock (_lockObj) 保证 _counter 的操作是“互斥”的;
  • 如果多个线程同时调用 Increase(),会按顺序排队进入 lock 里面。

为什么互斥是死锁必要条件?
如果一个资源可以被多个线程同时使用(比如只读资源、纯函数),线程之间就不会因为“抢资源”而卡住,也就不会形成死锁。


  1. 占有且等待(Hold and Wait)
    2.1 概念(白话版)

占有且等待意思是:线程已经拿着一个资源,还不放手;同时又在等待另一个资源。

类比:

  • A 拿着钥匙 1(资源 1),还要再借钥匙 2(资源 2)才能开第二个房间;
  • A 没拿到钥匙 2 之前,不会把钥匙 1 还回去;
  • 此时 A 就是“占有(钥匙 1)且等待(钥匙 2)”。

2.2 示例

private readonly object _lockA = new object(); private readonly object _lockB = new object(); public void Method1() { lock (_lockA) // 占有资源 A { Thread.Sleep(100); // 模拟业务 lock (_lockB) // 在占有 A 的情况下继续等待 B { // 使用资源 A 和 B 的临界区 } } }

在 Method1 中,线程先 lock (_lockA),在持有 A 的同时又 lock (_lockB),典型的“占有且等待”。

如果同时还有一个方法反过来(常见死锁写法):

public void Method2() { lock (_lockB) // 占有资源 B { Thread.Sleep(100); lock (_lockA) // 在占有 B 的情况下等待 A { // 使用资源 A 和 B 的临界区 } } }

当线程 T1 调用 Method1()、线程 T2 调用 Method2() 时,就具备了“占有且等待”的条件:

  • T1:占有 A 等待 B
  • T2:占有 B 等待 A

如果不存在“占有且等待”会怎样?

  • 比如设计成:线程请求新资源时,必须先释放已经持有的资源;
  • 那么线程不会同时持有多个资源,也就不存在资源之间形成“环形依赖”,死锁就难以出现。

3.不可抢占(No Preemption)
3.1 概念(白话版)

不可抢占意思是:线程持有的资源不能被强制剥夺,只能主动释放。
类比:

  • A 进了厕所,把门从里面反锁;
  • B 再急,也不能把门强行踹开,只能等 A 出来;
  • 只有 A 自己愿意开门,厕所才会被释放。

在 C# 中:

  • lock、Monitor、Mutex 等都不支持“强制释放”;
  • 如果线程挂死在临界区,锁就一直不释放。
    3.2 C# 示例
private readonly object _lockObj = new object(); public void DoWork() { lock (_lockObj) { // 一旦进来,其他线程就必须等待 // 这里如果发生异常或者死循环,锁就一直不释放 Thread.Sleep(Timeout.Infinite); // 模拟挂死 } } public void OtherWork() { lock (_lockObj) { // 永远等不到 Console.WriteLine("永远不会到达这里"); } }
  • 第一个线程进入 DoWork,获取 _lockObj 后一直 Sleep(模拟挂死);
  • 第二个线程在 OtherWork 中想获取同一个锁,只能永远等待。

如果资源可以被抢占,会怎样?
理论上,如果系统能“看你占着厕所不干事,就强制踢你出来”,就能打破死锁;
但在一般编程语言里,这样的强行剥夺很难安全实现,所以默认都是不可抢占。


  1. 循环等待(Circular Wait)
    4.1 概念(白话版)
    循环等待是死锁最直观的表现形式:线程 A 等待线程 B 的资源,线程 B 等待线程 A 的资源,多个线程之间形成了一个“环形的等待链”。

类比:

  • A 拿着钥匙 1 等钥匙 2;
  • B 拿着钥匙 2 等钥匙 3;
  • C 拿着钥匙 3 等钥匙 1;
  • A → B → C → A,形成一个等待环,谁也等不到。

两线程最简单的循环等待:

  • T1:持有 A,等待 B;
  • T2:持有 B,等待 A;
  • 等待关系:T1 → B → T2 → A → T1,形成环。

4.2 C# 示例(经典死锁示例)

private readonly object _lockA = new object(); private readonly object _lockB = new object(); public void Thread1Work() { lock (_lockA) // T1 持有 A { Console.WriteLine("T1:拿到 A,准备拿 B..."); Thread.Sleep(100); // 让 T2 有时间先拿 B lock (_lockB) // T1 等待 B { Console.WriteLine("T1:拿到 B"); } } } public void Thread2Work() { lock (_lockB) // T2 持有 B { Console.WriteLine("T2:拿到 B,准备拿 A..."); Thread.Sleep(100); // 让 T1 有时间先拿 A lock (_lockA) // T2 等待 A { Console.WriteLine("T2:拿到 A"); } } } 启动代码: public void Run() { var t1 = new Thread(Thread1Work); var t2 = new Thread(Thread2Work); t1.Start(); t2.Start(); t1.Join(); t2.Join(); }

执行过程:

  1. T1 先进入 Thread1Work(),获得 _lockA;
  2. T2 进入 Thread2Work(),获得 _lockB;
  3. T1 在尝试 lock (_lockB) 时,被阻塞,因为 B 已被 T2 持有;
  4. T2 在尝试 lock (_lockA) 时,被阻塞,因为 A 已被 T1 持有;
  5. T1 等 T2 的 B,T2 等 T1 的 A → 构成环形等待。

这就是一个完整的死锁,四个条件全部满足:

  1. 互斥:_lockA、_lockB 都是互斥资源(lock);
  2. 占有且等待:
  • T1:占有 A 等 B
  • T2:占有 B 等 A
  1. 不可抢占:A、B 在被持有时不能被强制夺回;
  2. 循环等待:T1 等 B(T2 持有)→ T2 等 A(T1 持有)→ 形成环。

  1. 综合示例:四个条件如何共同导致死锁?

我们用一个稍微完整的例子,把四个条件串起来看:

private readonly object _lockA = new object(); private readonly object _lockB = new object(); public void TaskA() { lock (_lockA) // 1. 互斥:获取互斥资源 A { Console.WriteLine("TaskA got A"); Thread.Sleep(100); // 2. 占有且等待:在持有 A 的状态下继续等待 B lock (_lockB) { Console.WriteLine("TaskA got B"); } } } public void TaskB() { lock (_lockB) // 1. 互斥:获取互斥资源 B { Console.WriteLine("TaskB got B"); Thread.Sleep(100); // 2. 占有且等待:在持有 B 的状态下继续等待 A lock (_lockA) { Console.WriteLine("TaskB got A"); } } } 运行: public void RunDeadlockDemo() { var tA = new Thread(TaskA); var tB = new Thread(TaskB); tA.Start(); tB.Start(); tA.Join(); tB.Join(); }

四个条件一一对照:

  1. 互斥条件
  • _lockA 和 _lockB 都通过 lock 实现互斥访问。
  1. 占有且等待
  • TaskA:先持有 _lockA,然后等待 _lockB;
  • TaskB:先持有 _lockB,然后等待 _lockA。
  1. 不可抢占
  • 一旦 TaskA 拿到 _lockA,除非它离开 lock 块,否则没有任何办法强制释放;
  • 同理,TaskB 拿到 _lockB 后也只能等它自己释放。
  1. 循环等待
  • TaskA 等 TaskB 手里的 _lockB;
  • TaskB 等 TaskA 手里的 _lockA;
  • 形成一个闭合等待圈。

只要这四个条件同时存在,死锁就有可能发生;
而真正发生死锁时,通常就是你没控制好锁的顺序或阻塞方式(比如 async 里用 .Result)。

  1. 怎样利用这“四个条件”去避免死锁?
  2. 破坏循环等待:
  • 统一锁顺序:所有地方获取多个锁时,规定好顺序,比如总是先锁 A 再锁 B;
  • 不允许有反过来“先 B 后 A”的情况。
  1. 缓和占有且等待:
  • 尽量避免:持有一个锁时再去申请另一个锁;
  • 多数业务可以重构为:先计算好数据,再在短小的临界区一次性拿锁、更新状态。
  1. 避免不必要的互斥:
  • 尽量使用不可变对象、多读少写、分区锁等方式减少需要“排队”的资源。
  1. 增加“抢占”效果(实质上是避免永久等待):
  • 使用 Monitor.TryEnter + 超时;
  • async 场景用 SemaphoreSlim.WaitAsync + CancellationToken;
  • 超时失败时打日志、回滚操作,相当于“主动放弃资源”,避免挂死。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

网站建设书江门网站建设

角色对应你的家 = 一家制造企业你家的冰箱 = 工厂的原材料仓库冰箱里的菜(土豆、鸡蛋、肉) = 工厂的原材料库存你每天做饭消耗的食材 =

2026/06/30 11:11:24

珠海网站建设广州市网站建设

终极指南:bilidown高效下载B站视频的完整解决方案【免费下载链接】bilidown哔哩哔哩视频解析下载工具,支持 8K 视频、Hi-Res 音频、杜比视界下载、批量解

2026/06/30 12:55:33

西安网站建设长沙营销型网站建设

随着深中一体化政策的深入推进,中山SEO公司迎来了新的机遇。在这一政策背景下,深圳与中山的产业协同更加紧密,尤其是在数字营销和SEO领域,跨区域

2026/06/30 14:17:39

网站建设流程摄影网站建设

深入解析 Exim 邮件传输代理:功能、配置与优化在当今数字化的时代,邮件作为一种重要的通信方式,其传输的稳定性和高效性至关重要。Exim 作为一款功能强大的邮件传输代理(MTA),为用户提供了丰富的

2026/06/30 13:01:34

信阳网站建设六安网站建设

💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的

2026/06/30 12:22:00

asp网站建设seo网站建设

OpenPose Editor实战手册:3步掌握AI人体姿态编辑核心技术【免费下载链接】openpose-editoropenpose-editor - 一个用于编辑和管理Openpos

2026/06/30 12:10:59

常州网站建设网站建设与维护

第一章:AI自动化新纪元的开启人工智能正以前所未有的速度重塑现代信息技术的格局,驱动各行各业迈向智能化转型。从自动化的数据处理到智能决策系统,AI不再仅仅是科

2026/06/30 11:12:24

建设厅网站福田网站建设

第一章:企业级权限架构的演进背景随着企业数字化转型的加速,系统复杂度与用户规模持续增长,传统的权限管理方式已难以满足现代应用对安全性、灵活性和可维护性的要求。

2026/06/30 12:41:32

网站建设 广州网站建设有限公司

在科研和工程领域,数据安全和软件许可证的安全性至关重要。LS-DYNA作为一款领先的有限元分析软件,深知许可证安全性的重要性。本文将探讨LS-DYNA许可证安全性方面的考虑

2026/06/30 11:48:27