周乐华 發表於 2026-2-1 17:26:00

死锁是怎么发生的,举个简单的例子

<h1 id="死锁的示例">死锁的示例</h1>
<p>下面就是一个会死锁的示例代码:</p>
<pre><code class="language-csharp">// 异步死锁示例 - 不使用 TaskScheduler,仅用多个 Task 互相等待

Console.WriteLine("=== 多 Task 互相等待死锁 ===\n");

// 两个任务互相用 .Result 等待对方完成 → 死锁
var tcsA = new TaskCompletionSource&lt;int&gt;();
var tcsB = new TaskCompletionSource&lt;int&gt;();

var taskA = Task.Run(() =&gt;
{
    Console.WriteLine($"[任务 A] 开始,线程 {Environment.CurrentManagedThreadId}");
    Console.WriteLine("[任务 A] 等待任务 B 的结果...");

    int resultB = tcsB.Task.Result; // A 阻塞等待 B

    Console.WriteLine($"[任务 A] 收到 B 的结果: {resultB}");
    tcsA.SetResult(1);
});

var taskB = Task.Run(() =&gt;
{
    Console.WriteLine($"[任务 B] 开始,线程 {Environment.CurrentManagedThreadId}");
    Console.WriteLine("[任务 B] 等待任务 A 的结果...");

    int resultA = tcsA.Task.Result; // B 阻塞等待 A

    Console.WriteLine($"[任务 B] 收到 A 的结果: {resultA}");
    tcsB.SetResult(2);
});

Console.WriteLine("等待所有任务完成(将在此处挂起)...\n");
Console.WriteLine("死锁原因:");
Console.WriteLine("- 任务 A 用 .Result 阻塞等待 tcsB.Task 完成");
Console.WriteLine("- 任务 B 用 .Result 阻塞等待 tcsA.Task 完成");
Console.WriteLine("- tcsA.SetResult() 要等 A 等到 B 之后才会执行");
Console.WriteLine("- tcsB.SetResult() 要等 B 等到 A 之后才会执行");
Console.WriteLine("- A 等 B,B 等 A → 死锁\n");

Task.WaitAll(taskA, taskB);

Console.WriteLine("若看到这行说明未发生死锁(本示例中应看不到)");

</code></pre>
<p>任务 A: 阻塞在 tcsB.Task.Result→等 B 完成<br>
任务 B: 阻塞在 tcsA.Task.Result→等 A 完成<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;↑_______________________________↓</p>
<ul>
<li>A 要等到 tcsB.SetResult(2) 被调用才会从 .Result 返回</li>
<li>B 要等到 tcsA.SetResult(1) 被调用才会从 .Result 返回</li>
<li>而 tcsA.SetResult(1) 在 A 里、tcsB.SetResult(2) 在 B 里,都要等对方先返回</li>
<li>所以谁都不会先完成 → 死锁</li>
</ul>
<h1 id="解决办法">解决办法</h1>
<p>这个代码运行起来肯定会死锁,当然实际项目中死锁的发生比这个要复杂,但是道理相似。<br>
解决办法就是把.Result这样的阻塞写法改成async await方式。</p>
<h1 id="线程池饥饿">线程池饥饿</h1>
<p>实际中可能是并发太高,线程池饥饿导致死锁,也就是:线程池里的工作线程数量有限。当大量任务都在阻塞等待(例如 .Result、.Wait()、Thread.Sleep()、lock 等)时,这些线程被占满且不会很快释放,新提交的任务只能在队列里等线程,就形成线程池饥饿:有活要干,但没有空闲线程来干。</p>


</div>
<div id="MySignature" role="contentinfo">
    <div id="AllanboltSignature">
<p id="PSignature" style="border-top-color: #e0e0e0; border-top-width: 1px; border-top-style: dashed; border-right-color: #e0e0e0; border-right-width: 1px; border-right-style: dashed; border-bottom-color: #e0e0e0; border-bottom-width: 1px; border-bottom-style: dashed; border-left-color: #e0e0e0; border-left-width: 1px; border-left-style: dashed; padding-top: 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 80px; background-image: url(https://images.cnblogs.com/cnblogs_com/pains/109838/r_copyright.png); background-attachment: initial; background-origin: initial; background-clip: initial; font-family: 微软雅黑; font-size: 11px; background-color: #e5f1f4; background-position: 1% 50%; background-repeat: no-repeat no-repeat; ">
作者:Rick Carter
<br />
出处:http://pains.cnblogs.com/
<br />
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
</p>
</div><br><br>
来源:https://www.cnblogs.com/pains/p/19559251
頁: [1]
查看完整版本: 死锁是怎么发生的,举个简单的例子