广州网站建设晋江网站建设

深圳市嘉嘉互娱科技有限公司 2026/09/09 17:30:43

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

西安网站建设公司襄樊网站建设

TinyOS 组件与接口技术深度解析1. 接口类型1.1 通用接口在编程中,接口是实现组件间交互的重要方式。通用接口是 nesC 语言支持的一种特殊接口类型,它以一个或多个类型作为参数。例如,Queu

2026/06/30 14:07:39

南充网站建设遵义网站建设

毕业季 AI 论文工具乱象丛生,有的主打 “快速生成” 却牺牲专业度,有的宣称 “全流程覆盖” 却功能碎片化。到底 AI 写论文哪个软件最好?抛开表面宣传&#

2026/06/30 10:15:19

宜昌网站建设辽宁省建设厅网站

第一章:Open-AutoGLM 离线运行技术支撑Open-AutoGLM 作为一款支持本地化部署的自动化语言模型框架,其离线运行能力依赖于多项核心技术的协同支持。为确保在

2026/06/30 11:28:55

泰州网站建设渭南网站建设

在计算机视觉项目开发中,数据标注质量往往决定模型性能上限。lllyasviel/Annotators项目集成了多个专业级预训练模型,为数据预处理和标注生成提供了完整解决方案

2026/06/30 12:35:02

网站建设运营官方网站建设

从毫伏到伏特:用三极管打造高性价比麦克风前置放大器你有没有试过把驻极体麦克风直接接到单片机的ADC引脚上,却发现几乎读不到有效信号?这几乎是每个嵌入式开发者在

2026/06/30 10:14:49

网站公司建设网站专业建设

PyTorch-CUDA-v2.7镜像对分布式训练的支持能力在现代AI研发中,一个常见的场景是:团队拿到了一批新卡(比如A100),

2026/06/30 13:22:05

网站建设空间东营网站建设

Linux 提供了各种命令来执行不同类型的系统关闭。然而,用于指代这些关闭类型的术语可能会让人困惑,尤其是对 Linux 新用户来说。在本文中,我们将讨论关闭

2026/06/30 11:16:54

信阳网站建设门户网站建设方案

WindowResizer 2025:窗口尺寸调整的终极解决方案【免费下载链接】WindowResizer一个可以强制调整应用程序窗口大小的工具项目地址: https://gitcode

2026/06/30 11:46:27

建设网站制作信阳网站建设

5分钟掌握QR Code Monster v2:创意二维码终极指南【免费下载链接】control_v1p_sd15_qrcode_monster项目地址: https://ai.gitc

2026/06/30 13:44:07

外贸网站建设江津网站建设

Maya动画重定向工具:3分钟学会跨角色动画迁移技术【免费下载链接】animation-retargeting-toolAnimation retargeting tool for Au

2026/06/30 13:53:08