C#中Task.Wait()、Task.Result、Task.GetAwaiter().GetResult()区别
<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>一、概念对比</li><li>二、底层机制分析</li><ul class="second_class_ul"><li>1、 Task.Wait()</li><li>2、Task.Result</li><li>3、 Task.GetAwaiter().GetResult()</li></ul><li>三、线程行为对比</li><ul class="second_class_ul"></ul><li>四、异常处理差异</li><ul class="second_class_ul"></ul><li>五、潜在风险</li><ul class="second_class_ul"></ul><li>六、总结</li><ul class="second_class_ul"></ul></ul></div><p class="maodian"></p><h2>一、概念对比</h2><table><thead><tr><th>方法/属性</th><th>阻塞线程</th><th>调用 TaskAwaiter continuation</th><th>异常处理</th><th>典型用途</th></tr></thead><tbody><tr><td>Task.Wait()</td><td>✅ 阻塞调用线程</td><td>❌ 不触发 continuation</td><td>抛 AggregateException</td><td>同步等待 Task 完成,不关心结果</td></tr><tr><td>Task.Result</td><td>✅ 阻塞调用线程</td><td>❌ 不触发 continuation</td><td>抛 AggregateException 包含 Task 异常</td><td>同步获取 Task 结果</td></tr><tr><td>Task.GetAwaiter().GetResult()</td><td>✅ 阻塞调用线程</td><td>❌ 不触发 continuation</td><td>直接抛原始异常(不包 AggregateException)</td><td>内部 await/框架调用同步获取 Task 结果,更精确异常</td></tr></tbody></table>
<p class="maodian"></p><h2>二、底层机制分析</h2>
<p class="maodian"></p><h3>1、 Task.Wait()</h3>
<div class="jb51code"><pre class="brush:csharp;">public void Wait() {
if (!IsCompleted)
WaitHelper.Wait(this, Timeout.Infinite, cancellationToken: default);
}
</pre></div>
<ul><li>使用 <code>WaitHelper.Wait()</code>,内部通过 <code>ManualResetEventSlim</code> 或 <code>WaitHandle</code> 阻塞调用线程。</li><li><strong>阻塞当前线程</strong>,等待 Task 完成(RunToCompletion/Faulted/Canceled)。</li><li>不触发 TaskAwaiter 的 continuation。</li></ul>
<p class="maodian"></p><h3>2、Task.Result</h3>
<div class="jb51code"><pre class="brush:csharp;">public T Result {
get {
Wait(); // 调用同步等待
return GetResult(); // 返回 Task<T> 结果
}
}
</pre></div>
<ul><li><p>内部先阻塞等待 Task 完成(调用 Wait())。</p></li><li><p>然后返回 Task 结果或抛异常。</p></li><li><p>对异常处理不同:</p>
<ul><li><code>Wait()</code> 或 <code>Result</code> 会把 Task 异常 <strong>封装为 AggregateException</strong>。</li></ul></li></ul>
<p class="maodian"></p><h3>3、 Task.GetAwaiter().GetResult()</h3>
<div class="jb51code"><pre class="brush:csharp;">var awaiter = task.GetAwaiter();
awaiter.GetResult();
</pre></div>
<ul><li><p>不阻塞 Task 的内部状态管理,直接使用 TaskAwaiter 的 <code>GetResult()</code>:</p>
<ul><li>如果 Task 未完成 → 会阻塞调用线程等待完成</li><li>如果 Task 完成 → 直接返回结果或抛异常</li></ul></li><li><p>与 <code>Task.Result</code> 最大区别:</p>
<ul><li><strong>异常不会包装成 AggregateException</strong>,而是抛原始异常</li></ul></li><li><p>不触发 async continuation</p></li></ul>
<p class="maodian"></p><h2>三、线程行为对比</h2>
<div class="jb51code"><pre class="brush:plain;">Caller Thread
├─ Task.Wait() / Task.Result / GetAwaiter().GetResult()
| -> 阻塞当前线程
Task completes (ThreadPool/IOCP)
├─ Task signals wait handle
Caller Thread unblocks
├─ Wait() 返回 / Result 返回 / GetResult() 返回或抛异常
</pre></div>
<blockquote><p>注意:<strong>无论哪种方式,continuation 不会在阻塞线程上执行</strong>。</p>
<ul><li>async/await 的 continuation 依赖 OnCompleted / SynchronizationContext 调度</li><li>直接同步阻塞方法绕过了这些机制</li></ul></blockquote>
<p class="maodian"></p><h2>四、异常处理差异</h2>
<table><thead><tr><th>方法</th><th>异常类型</th><th>包装情况</th></tr></thead><tbody><tr><td>Task.Wait()</td><td>Task 异常</td><td>AggregateException</td></tr><tr><td>Task.Result</td><td>Task 异常</td><td>AggregateException</td></tr><tr><td>Task.GetAwaiter().GetResult()</td><td>Task 异常</td><td>原始异常(unwrap)</td></tr></tbody></table>
<blockquote><p>⚠️ 这是为什么 GetAwaiter().GetResult() 更常在库内部使用,因为它可以避免额外的 AggregateException 包装,保持异常原样。</p></blockquote>
<p class="maodian"></p><h2>五、潜在风险</h2>
<ol><li><p><strong>死锁</strong></p>
<ul><li>在 UI 线程调用 <code>Wait()</code> / <code>Result</code>,而 Task continuation 捕获 SynchronizationContext → continuation 无法执行 → 死锁。</li></ul></li><li><p><strong>线程阻塞</strong></p>
<ul><li>阻塞调用线程,浪费资源,ThreadPool 扩展可能触发饥饿。</li></ul></li></ol>
<p class="maodian"></p><h2>六、总结</h2>
<ol><li><strong>Task.Wait()</strong> → 阻塞等待,无返回值,异常 AggregateException</li><li><strong>Task.Result</strong> → 阻塞等待,返回结果,异常 AggregateException</li><li><strong>Task.GetAwaiter().GetResult()</strong> → 阻塞等待,返回结果或抛原始异常,内部 await/框架常用</li><li><strong>都不触发 continuation</strong>,阻塞线程可能导致死锁</li><li><strong>async/await</strong> 才是非阻塞、自动调度 continuation 的机制</li></ol>
<p>到此这篇关于C#中Task.Wait()、Task.Result、Task.GetAwaiter().GetResult()区别的文章就介绍到这了,更多相关Task.Wait()、Task.Result、Task.GetAwaiter().GetResult()内容请搜索琼殿技术社区以前的文章或继续浏览下面的相关文章希望大家以后多多支持琼殿技术社区!</p>
<div class="art_xg">
<b>您可能感兴趣的文章:</b><ul><li>c# Task.Wait()与awaiat Task异常处理的区别说明</li></ul>
</div>
</div>
<!--endmain-->
頁:
[1]