荆州网站建设衡阳网站建设

安徽恒顶安全科技有限公司 2026/09/09 20:35:44

在 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;
  • 超时失败时打日志、回滚操作,相当于“主动放弃资源”,避免挂死。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

绍兴网站建设长沙营销型网站建设

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等

2026/06/30 09:49:47

信阳网站建设企业网站建设的

当你的iPhone陷入激活锁困境时,AppleRa1n提供了一种简单有效的解决方案。这款专业工具专门针对iOS 15-16系统的设备设计,能够帮助用户在离线环境下快速解决激

2026/06/30 11:36:56

诸城网站建设陕西网站建设

规避损失 > 追求收益,这是几乎所有决策偏差的源头。不确定的好处,不如确定的安全;可能的失败,比可能的成功更让人恐惧。所以人们会ÿ

2026/06/30 12:51:03

晋江网站建设龙华网站建设

HunyuanVideo-Foley Docker部署指南:一键启动视频智能音效引擎 🎧在短视频、影视后期和游戏开发领域,一个常被忽视却至关重要的环节正在悄

2026/06/30 12:57:03

滁州网站建设大良网站建设

5步轻松搭建个人音乐库:洛雪音源完整配置教程【免费下载链接】lxmusic-lxmusic(洛雪音乐)全网最新最全音源项目地址: https://gitcode.com/gh_mirro

2026/06/30 13:49:37

pc网站建设惠州网站建设

Spring Boot基于大数据的高乐健身器材销售数据可视化系统是一个集成了大数据处理、数据分析和数据可视化技术的综合性系统,旨在为高乐健身器材企业提供全面、直观、高效的销售数据洞察与分

2026/06/30 11:20:55

网站建设制作广州网站建设工作室

从“走线熔断”说起:如何科学设计电源层PCB线宽你有没有遇到过这样的情况?一块刚打回来的PCB板,上电测试没几分钟,电源路径附近突然冒烟

2026/06/30 11:12:24

黄石网站建设恩施网站建设

本文提供了大模型产品经理的完整学习路线,包括计算机科学基础、人工智能与机器学习基础、大模型技术概览与优化、产品管理与商业分析、实战经验积累以及持续学习与自我提升等阶段。同时分享了免费学习

2026/06/30 12:54:03

建设部网站泰州网站建设

一键打包下载:让批量视频导出不再“卡手”在数字人技术飞速落地的今天,越来越多企业开始用AI批量生成培训视频、客服应答片段或电商商品介绍。想象这样一个场景:某教

2026/06/30 13:56:08

重庆网站建设义乌网站建设

YOLO模型训练断点续传功能实现:网络不稳定也不怕在工业级AI视觉系统中,目标检测的稳定性与效率直接决定着产品能否顺利落地。YOLO(You Only Loo

2026/06/30 12:05:29