6
社区成员
发帖
与我相关
我的任务
分享您是否正在准备涉及 C# 异步编程的面试?你来对地方了!这篇内容全面的文章涵盖了广泛的 C# async-await 面试问题,这些问题将测试您对 async-await 模式的理解,以及帮助您提高技能的解决方案和示例。
从基本概念到高级技术,本文涵盖了您在技术面试中可能遇到的 C# 面试问题中的异步和等待。
ConfigureAwait(false)主要 区别是什么?ConfigureAwait(true)ConfigureAwait是一种用于await控制异步方法的执行在等待的任务完成后应如何继续的方法。它采用布尔值作为参数,允许开发人员指定是继续捕获的同步上下文还是其他上下文。
ConfigureAwait(false)当您不想在等待的任务完成后在捕获的同步上下文上恢复执行时使用。在许多情况下,这用于防止死锁和提高性能,尤其是对于库或非 UI 代码。
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">someTask</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">ConfigureAwait</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">false</span><span style="color:var(--syntax-text-color)">);</span>
</code></span></span>
ConfigureAwait(true)用于维护同步上下文(如果有的话)并在该上下文中恢复执行等待的任务。ConfigureAwait这是未显式调用时的默认行为。它在您想要维护捕获的上下文的情况下很有用,例如更新 UI 组件或更改由多个线程共享的全局状态。您很少需要显式使用,ConfigureAwait(true)因为这是默认行为,但这是一个示例:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">someTask</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">ConfigureAwait</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">true</span><span style="color:var(--syntax-text-color)">);</span>
</code></span></span>
注意:在基于 UI 的应用程序中,捕获的上下文将是 UI 上下文(例如 WPF 或 WinForms 应用程序中的主 UI 线程)。在这种情况下,必须在执行 UI 更新时维护同步上下文,因此您可以使用ConfigureAwait(true)或ConfigureAwait根本不使用。
Task.WhenAll工作,以及在 C# 中使用它会产生哪些潜在问题?Task.WhenAll是类提供的一种方法Task,它接受一个IEnumerable<Task>或一组Task实例作为参数,并返回一个新的Task,一旦所有输入任务完成就完成。
用途:
潜在问题:
Task.WhenAll,各个任务抛出的异常被包装在AggregateException. AggregateException处理此问题并将其解包以了解任务中发生的实际异常至关重要否则,您可能会错过关键异常或遇到意外行为。
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code> <span style="color:var(--syntax-declaration-color)">try</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">Task</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">WhenAll</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">task1</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">task2</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">task3</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">catch</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">AggregateException</span> <span style="color:var(--syntax-text-color)">ae</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-comment-color)">// Handle the individual exceptions using Unwrap or InnerExceptions</span>
<span style="color:var(--syntax-declaration-color)">foreach</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">innerException</span> <span style="color:var(--syntax-declaration-color)">in</span> <span style="color:var(--syntax-text-color)">ae</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">InnerExceptions</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-comment-color)">// Handle each exception as necessary</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
SemaphoreSlim或其他并发控制方法来限制并发任务数。Task.WhenAll,并考虑限制可用资源的并发以避免潜在问题。当您在异步方法上同步阻塞时,异步等待可能会发生死锁,导致调用者和被调用者相互等待,从而有效地导致死锁。
想象一下基于 UI 的应用程序中的以下场景:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">DoSomethingAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">SomeOperationAsync</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-comment-color)">// ... more code here</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">void</span> <span style="color:var(--syntax-name-color)">ButtonClickHandler</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">object</span> <span style="color:var(--syntax-text-color)">sender</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">RoutedEventArgs</span> <span style="color:var(--syntax-text-color)">e</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-name-color)">DoSomethingAsync</span><span style="color:var(--syntax-text-color)">().</span><span style="color:var(--syntax-name-color)">Wait</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-comment-color)">// ... more code</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
该ButtonClickHandler方法正在等待DoSomethingAsync完成。同时,DoSomethingAsync正在等待SomeOperationAsync()完成。因为该await语句默认捕获同步上下文(在本例中为 UI 上下文),所以继续需要SomeOperationAsyncUI 上下文。但是,UI 上下文被对 的调用阻塞DoSomethingAsync().Wait(),因此发生了死锁。
为避免死锁,请遵循以下准则:
.Wait()或使用阻塞调用.Result。相反,使用异步并await一直向上:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code> <span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-declaration-color)">void</span> <span style="color:var(--syntax-name-color)">ButtonClickHandler</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">object</span> <span style="color:var(--syntax-text-color)">sender</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">RoutedEventArgs</span> <span style="color:var(--syntax-text-color)">e</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">DoSomethingAsync</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-comment-color)">// ... more code</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
ConfigureAwait(false),尤其是在库代码中或当您不需要依赖捕获的同步上下文时。这可以防止在对异步方法进行阻塞调用时出现死锁。IAsyncDisposable?它通常如何与 C# 中的 async-await 一起使用?该IAsyncDisposable接口用于提供释放非托管资源的异步机制,而该IDisposable接口用于同步资源清理。使用异步操作时,IAsyncDisposable允许您执行非阻塞清理任务。
实施IAsyncDisposable要求您提供一种async ValueTask DisposeAsync()方法。通常,IAsyncDisposable与具有异步方法的类或对象一起使用,并且还保留非托管资源,例如文件句柄、网络连接或数据库连接。
要使用类实现IAsyncDisposable,您可以使用await using语句,它确保以异步和非阻塞方式释放资源:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">PerformOperationAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-declaration-color)">using</span> <span style="color:var(--syntax-text-color)">var</span> <span style="color:var(--syntax-text-color)">resource</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-name-color)">MyAsyncDisposableResource</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">resource</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">ExecuteAsync</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-comment-color)">// ... more code</span>
<span style="color:var(--syntax-comment-color)">// The DisposeAsync method is called automatically when leaving the scope.</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
请记住,在实现的同时IAsyncDisposable,您还应该实现IDisposable允许用户在需要时同步处理资源的接口。
ValueTask结构与 async-await 中的类有何不同Task,并提供一个可能更适合使用它的场景。结构ValueTask体代表一个操作,将来会产生一个结果,类似于类Task。但是,主要区别在于它ValueTask是值类型(结构),而它Task是引用类型(类)。这意味着ValueTask实例通常是在堆栈上创建的,并且与Task.
ValueTask主要设计用于高性能场景或希望方法在大多数时间同步返回的情况。当大多数调用不需要异步开销时,使用ValueTaskinstead of可以帮助减少内存分配并提高性能。Task
示例场景:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">ValueTask</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-declaration-color)">int</span><span style="color:var(--syntax-text-color)">></span> <span style="color:var(--syntax-name-color)">PerformOperationAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">if</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">cache</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">TryGetFromCache</span><span style="color:var(--syntax-text-color)">(</span> <span style="color:var(--syntax-declaration-color)">out</span> <span style="color:var(--syntax-declaration-color)">int</span> <span style="color:var(--syntax-text-color)">result</span><span style="color:var(--syntax-text-color)">))</span>
<span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-text-color)">result</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-text-color)">result</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">ComputeResultAsync</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-text-color)">cache</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">StoreInCache</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">result</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-text-color)">result</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
在这个例子中,我们使用是ValueTask<int>因为我们希望大多数时候,值将从缓存中同步返回。当该值在缓存中不可用时,我们才执行异步操作(ComputeResultAsync)。ValueTask<int>当从缓存返回值时,使用将减少方法调用的内存分配。
重要的是要注意,ValueTask不应该总是默认选择Task. 您应该只考虑在性能关键场景中使用ValueTask,并在仔细分析权衡和对 API 使用的潜在影响之后。避免在不必要时使用asyncand awaitwithValueTask会导致不明显的逻辑错误和意外行为。
ValueTask现在我们已经介绍了 async-await 的基础知识以及和之间的区别Task,是时候深入研究更高级的技术了,比如异步并行的概念。
采用并行策略可以进一步提高异步代码的效率和性能。继续阅读以探索这种方法的优点和潜在缺点。
异步并行是指同时执行多个异步操作,通常目的是通过在更短的总体时间内完成任务来提高性能。在 C# 中,您可以使用各种技术实现异步并行性,包括Task.WhenAll将SemaphoreSlim类与多个实例结合使用async Task。
Task.WhenAll下面是一个使用来实现异步并行的例子:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">ProcessMultipleItemsAsync</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">IEnumerable</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">Item</span><span style="color:var(--syntax-text-color)">></span> <span style="color:var(--syntax-text-color)">items</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">tasks</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-text-color)">items</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">Select</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">item</span> <span style="color:var(--syntax-text-color)">=></span> <span style="color:var(--syntax-name-color)">ProcessItemAsync</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">item</span><span style="color:var(--syntax-text-color)">));</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">Task</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">WhenAll</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">tasks</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
优点:
缺点:
TaskCompletionSource帮助在 C# 中将非异步代码与 async-await 集成,使用此技术时需要注意哪些潜在陷阱?该类TaskCompletionSource<TResult>允许您将使用回调或其他非等待模式的非异步代码包装到Task<TResult>可以等待的代码中。
TaskCompletionSource提供Task表示正在进行的操作的属性和一组方法来设置操作的结果、异常或取消,通常在回调或事件处理程序中使用。
TaskCompletionSource下面是一个用于包装非异步计时器的示例:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">WaitForTimeoutAsync</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">TimeSpan</span> <span style="color:var(--syntax-text-color)">timeout</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">tcs</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-text-color)">TaskCompletionSource</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-declaration-color)">bool</span><span style="color:var(--syntax-text-color)">>();</span>
<span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">timer</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-text-color)">System</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Timers</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">Timer</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">timeout</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">TotalMilliseconds</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-text-color)">AutoReset</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">false</span> <span style="color:var(--syntax-text-color)">};</span>
<span style="color:var(--syntax-text-color)">timer</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Elapsed</span> <span style="color:var(--syntax-text-color)">+=</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">sender</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">args</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-text-color)">=></span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">tcs</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">SetResult</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">true</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">timer</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">Dispose</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-text-color)">};</span>
<span style="color:var(--syntax-text-color)">timer</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">Start</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">tcs</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Task</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
潜在的陷阱:
TaskCompletionSource(ConfigureAwait(false)尽可能使用)。否则,您可能会引入死锁或与同步上下文相关性相关的其他问题。TaskCompletionSource,请确保正确处理异常,方法是将它们传递给SetException方法或在回调方法中处理它们。TaskCompletionSource:在将非异步代码(例如,事件处理程序、计时器、网络连接)用于将其转换为异步等待代码时,请小心清理非异步代码使用的任何资源,以防止泄漏或不必要的副作用。SemaphoreSlim,如何在C#应用程序中正确使用它来控制并发?SemaphoreSlim是一种轻量级、异步兼容的信号量,可用于限制对共享资源的并发访问或控制在异步等待场景中执行的并行度。
SemaphoreSlim当您的资源数量有限或需要控制并发异步操作的数量以防止耗尽、高内存使用率或其他性能问题时,该类是合适的。
SemaphoreSlim下面是使用限制并发数据库读取数的示例:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">private</span> <span style="color:var(--syntax-declaration-color)">static</span> <span style="color:var(--syntax-declaration-color)">readonly</span> <span style="color:var(--syntax-text-color)">SemaphoreSlim</span> <span style="color:var(--syntax-text-color)">Semaphore</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-name-color)">SemaphoreSlim</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-literal-color)">5</span><span style="color:var(--syntax-text-color)">);</span> <span style="color:var(--syntax-comment-color)">// Limit concurrency to 5 operations</span>
<span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">IEnumerable</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">Data</span><span style="color:var(--syntax-text-color)">>></span> <span style="color:var(--syntax-name-color)">ReadFromDatabaseAsync</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">int</span> <span style="color:var(--syntax-text-color)">id</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">Semaphore</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">WaitAsync</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-declaration-color)">try</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-comment-color)">// Execute the database read operation</span>
<span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">data</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">ExecuteDatabaseQueryAsync</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">id</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-text-color)">data</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">finally</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">Semaphore</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">Release</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
使用 控制并发时SemaphoreSlim,请记住:
WaitAsync()和Release()方法,合理释放资源。SemaphoreSlim与节流、批处理或重试策略等其他机制相 结合,以确保更强大的并发解决方案。async-await 场景中的异常处理与同步场景中的异常处理类似,主要区别在于使用了async Taskinstead of 同步方法。使用块以与同步方法相同的方式捕获并重新抛出异步方法中的异常[try-catch](https://www.bytehide.com/blog/try-catch-csharp/ "Master Try Catch in C# (The Definitive Guide)")。主要区别在于异步方法中发生的异常在等待结果任务时传播。
以下是在 async-await 场景下处理异常的一些常见做法:
try-catch在异步方法中使用块:使用 try-catch 块保护异步代码的关键部分,就像使用同步代码一样。
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">DoWorkAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">try</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">SomeOperationAsync</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">catch</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">SomeException</span> <span style="color:var(--syntax-text-color)">ex</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-comment-color)">// Handle the exception</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
await:等待任务时,您可以直接在await语句上捕获异常。
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">PerformOperationAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">try</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">DoWorkAsync</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">catch</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">SomeException</span> <span style="color:var(--syntax-text-color)">ex</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-comment-color)">// Handle the exception</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
Task.WhenAll并发处理多个任务时,使用 try-catch 块来处理AggregateException任何任务抛出的异常。AggregateException根据需要 解包以处理每个单独的异常。
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">PerformMultipleOperationsAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">tasks</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-text-color)">Task</span><span style="color:var(--syntax-text-color)">[]</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-name-color)">Operation1Async</span><span style="color:var(--syntax-text-color)">(),</span>
<span style="color:var(--syntax-name-color)">Operation2Async</span><span style="color:var(--syntax-text-color)">(),</span>
<span style="color:var(--syntax-name-color)">Operation3Async</span><span style="color:var(--syntax-text-color)">(),</span>
<span style="color:var(--syntax-text-color)">};</span>
<span style="color:var(--syntax-declaration-color)">try</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">Task</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">WhenAll</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">tasks</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">catch</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">AggregateException</span> <span style="color:var(--syntax-text-color)">ae</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">foreach</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">innerException</span> <span style="color:var(--syntax-declaration-color)">in</span> <span style="color:var(--syntax-text-color)">ae</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">InnerExceptions</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-comment-color)">// Handle each exception as necessary</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
ConfigureAwait(false)与 try-catch 一起使用:在库代码或非 UI 场景中使用 try-catch 块时,ConfigureAwait(false)在等待任务时使用,以防止捕获同步上下文并导致潜在的死锁。
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">DoWorkAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">try</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">SomeOperationAsync</span><span style="color:var(--syntax-text-color)">().</span><span style="color:var(--syntax-name-color)">ConfigureAwait</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">false</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">catch</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">SomeException</span> <span style="color:var(--syntax-text-color)">ex</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-comment-color)">// Handle the exception</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
when在 catch 子句中使用关键字引入了异常过滤器。这有助于根据异常状态或其他因素将条件逻辑添加到异常处理中。
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">DoWorkAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">try</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">SomeOperationAsync</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">catch</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">SomeException</span> <span style="color:var(--syntax-text-color)">ex</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-name-color)">when</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">ex</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">ErrorCode</span> <span style="color:var(--syntax-text-color)">==</span> <span style="color:var(--syntax-literal-color)">404</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-comment-color)">// Handle the specific case where the error code is 404</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
Task.Yield,建议在哪些情况下使用该方法?该Task.Yield方法返回一个可等待YieldAwaitable结构,当等待时,该结构会导致异步方法暂停其执行并立即将控制权交还给调用方法。这允许在异步方法等待YieldAwaitable.
Task.Yield在需要确保长时间运行或迭代异步操作不会独占当前线程或同步上下文的情况下特别有用,否则会阻塞其他操作或降低应用程序的性能。
常见用例包括Task.Yield:
await Task.Yield();语句,您可以通过定期将控制权交还给 UI 线程来让 UI 保持响应。
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">PerformLongRunningOperationAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">for</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">int</span> <span style="color:var(--syntax-text-color)">i</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-literal-color)">0</span><span style="color:var(--syntax-text-color)">;</span> <span style="color:var(--syntax-text-color)">i</span> <span style="color:var(--syntax-text-color)"><</span> <span style="color:var(--syntax-literal-color)">100</span><span style="color:var(--syntax-text-color)">;</span> <span style="color:var(--syntax-text-color)">i</span><span style="color:var(--syntax-text-color)">++)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-comment-color)">// Perform a partial computation</span>
<span style="color:var(--syntax-name-color)">DoPartialComputation</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-comment-color)">// Yield control back to the calling context (e.g., the UI thread)</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">Task</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">Yield</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
Task.Yield可以帮助确保更公平地调度工作项并防止资源垄断。
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">PerformLongRunningOperationAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">for</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">int</span> <span style="color:var(--syntax-text-color)">i</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-literal-color)">0</span><span style="color:var(--syntax-text-color)">;</span> <span style="color:var(--syntax-text-color)">i</span> <span style="color:var(--syntax-text-color)"><</span> <span style="color:var(--syntax-literal-color)">100</span><span style="color:var(--syntax-text-color)">;</span> <span style="color:var(--syntax-text-color)">i</span><span style="color:var(--syntax-text-color)">++)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-comment-color)">// Perform a partial computation that requires shared resources</span>
<span style="color:var(--syntax-name-color)">DoPartialComputationWithSharedResource</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-comment-color)">// Yield control back to the calling context to allow other tasks to access shared resources</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">Task</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">Yield</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
请记住,使用Task.Yield会引入额外的开销,并且可能并不总是必要的。必须仔细分析特定的用例和情况,以确定是否Task.Yield会提高应用程序的性能和响应能力或导致不必要的延迟。
在探索了 C# 中的各种异步等待模式和技术之后,了解如何将异步等待应用到语言的其他方面(例如LINQ查询)至关重要。
异步编程会显着影响查询的执行,因此让我们讨论一下在将 async-await 与 LINQ 结合使用时需要采取的预防措施。
将 LINQ 与 async-await 结合使用时,请务必注意大多数标准 LINQ 运算符(例如Where、 、 等)不具有对异步Select操作GroupBy的内置支持。使用标准运算符的 LINQ 查询是同步执行的,这在异步方法中执行时可能会导致阻塞问题,尤其是在处理大型数据集时。
要使用 LINQ 处理异步处理,请考虑以下预防措施和最佳实践:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code> <span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">PerformLinqQueryAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-comment-color)">// Fetch data asynchronously</span>
<span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">data</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">FetchDataAsync</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-comment-color)">// Perform LINQ query (synchronously)</span>
<span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">results</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-text-color)">data</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">Where</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">x</span> <span style="color:var(--syntax-text-color)">=></span> <span style="color:var(--syntax-text-color)">x</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Value</span> <span style="color:var(--syntax-text-color)">></span> <span style="color:var(--syntax-literal-color)">10</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-comment-color)">// Process/query results asynchronously where necessary</span>
<span style="color:var(--syntax-comment-color)">// ...</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code> <span style="color:var(--syntax-declaration-color)">using</span> <span style="color:var(--syntax-text-color)">System.Linq.Async</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">PerformLinqQueryAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">IAsyncEnumerable</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">Data</span><span style="color:var(--syntax-text-color)">></span> <span style="color:var(--syntax-text-color)">data</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-name-color)">FetchDataAsyncEnumerable</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-comment-color)">// Perform asynchronous LINQ query</span>
<span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">results</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">data</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">Where</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">x</span> <span style="color:var(--syntax-text-color)">=></span> <span style="color:var(--syntax-text-color)">x</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Value</span> <span style="color:var(--syntax-text-color)">></span> <span style="color:var(--syntax-literal-color)">10</span><span style="color:var(--syntax-text-color)">).</span><span style="color:var(--syntax-name-color)">ToListAsync</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
ToListAsync()或之类的方法显式异步地具体化查询结果。ToArrayAsync()
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code> <span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">PerformLinqQueryAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">IAsyncEnumerable</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">Data</span><span style="color:var(--syntax-text-color)">></span> <span style="color:var(--syntax-text-color)">data</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-name-color)">FetchDataAsyncEnumerable</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-comment-color)">// Perform asynchronous LINQ query and materialize the results explicitly</span>
<span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">results</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">data</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">Where</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">x</span> <span style="color:var(--syntax-text-color)">=></span> <span style="color:var(--syntax-text-color)">x</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Value</span> <span style="color:var(--syntax-text-color)">></span> <span style="color:var(--syntax-literal-color)">10</span><span style="color:var(--syntax-text-color)">).</span><span style="color:var(--syntax-name-color)">ToListAsync</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-comment-color)">// Process/query results asynchronously where necessary</span>
<span style="color:var(--syntax-comment-color)">// ...</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
双向异步通信是指服务端和客户端都可以异步发送和接收消息的通信模式。要在 C# 中实现这一点,您可以使用各种技术,包括 WebSocket、SignalR、gRPC 或构建在 TCP 或 UDP 之上的自定义协议。
下面是使用 WebSocket 和 async-await 进行双向通信的示例:
服务器端:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">HandleClientAsync</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">WebSocket</span> <span style="color:var(--syntax-text-color)">socket</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">buffer</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-declaration-color)">byte</span><span style="color:var(--syntax-text-color)">[</span><span style="color:var(--syntax-literal-color)">4096</span><span style="color:var(--syntax-text-color)">];</span>
<span style="color:var(--syntax-declaration-color)">while</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">socket</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">State</span> <span style="color:var(--syntax-text-color)">==</span> <span style="color:var(--syntax-text-color)">WebSocketState</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Open</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">receivedResult</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">socket</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">ReceiveAsync</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-text-color)">ArraySegment</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-declaration-color)">byte</span><span style="color:var(--syntax-text-color)">>(</span><span style="color:var(--syntax-text-color)">buffer</span><span style="color:var(--syntax-text-color)">),</span> <span style="color:var(--syntax-text-color)">CancellationToken</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">None</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-comment-color)">// Process the received message</span>
<span style="color:var(--syntax-comment-color)">// ...</span>
<span style="color:var(--syntax-comment-color)">// Send a response to the client asynchronously</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">socket</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">SendAsync</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-text-color)">ArraySegment</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-declaration-color)">byte</span><span style="color:var(--syntax-text-color)">>(</span><span style="color:var(--syntax-text-color)">buffer</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-literal-color)">0</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">receivedResult</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Count</span><span style="color:var(--syntax-text-color)">),</span> <span style="color:var(--syntax-text-color)">WebSocketMessageType</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Text</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">receivedResult</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">EndOfMessage</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">CancellationToken</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">None</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">socket</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">CloseAsync</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">WebSocketCloseStatus</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">NormalClosure</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-string-color)">"Communication completed."</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">CancellationToken</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">None</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
客户端:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">CommunicateWithServerAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">using</span> <span style="color:var(--syntax-text-color)">var</span> <span style="color:var(--syntax-text-color)">client</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-name-color)">ClientWebSocket</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">client</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">ConnectAsync</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-name-color)">Uri</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"ws://mywebsocketservice"</span><span style="color:var(--syntax-text-color)">),</span> <span style="color:var(--syntax-text-color)">CancellationToken</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">None</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-comment-color)">// Send a message to the server asynchronously</span>
<span style="color:var(--syntax-declaration-color)">byte</span><span style="color:var(--syntax-text-color)">[]</span> <span style="color:var(--syntax-text-color)">message</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-text-color)">Encoding</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">UTF8</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">GetBytes</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"Hello, server!"</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">client</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">SendAsync</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-text-color)">ArraySegment</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-declaration-color)">byte</span><span style="color:var(--syntax-text-color)">>(</span><span style="color:var(--syntax-text-color)">message</span><span style="color:var(--syntax-text-color)">),</span> <span style="color:var(--syntax-text-color)">WebSocketMessageType</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Text</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-declaration-color)">true</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">CancellationToken</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">None</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-comment-color)">// Receive a message from the server asynchronously</span>
<span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">buffer</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-declaration-color)">byte</span><span style="color:var(--syntax-text-color)">[</span><span style="color:var(--syntax-literal-color)">4096</span><span style="color:var(--syntax-text-color)">];</span>
<span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">receivedResult</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">client</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">ReceiveAsync</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-text-color)">ArraySegment</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-declaration-color)">byte</span><span style="color:var(--syntax-text-color)">>(</span><span style="color:var(--syntax-text-color)">buffer</span><span style="color:var(--syntax-text-color)">),</span> <span style="color:var(--syntax-text-color)">CancellationToken</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">None</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-comment-color)">// Process the received message</span>
<span style="color:var(--syntax-comment-color)">// ...</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
重要注意事项:
IAsyncEnumerable<T>接口对大数据流进行高效的异步处理?异步流是 C# 8.0 中引入的一项 C# 功能,它允许您异步迭代数据流。异步流不是将整个数据集加载到内存中,而是通过将 async-await 的强大功能与传统的 IEnumerable 模式相结合,以异步方式一次处理一个项目。
接口IAsyncEnumerable<T>是异步流的核心元素,它代表了一个可以一次枚举一个项目的异步集合。要使用和生成异步流,分别使用await foreach语句和异步迭代器方法。yield return
例如,让我们创建一个生成 IAsyncEnumerable 项的方法:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">IAsyncEnumerable</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-declaration-color)">int</span><span style="color:var(--syntax-text-color)">></span> <span style="color:var(--syntax-name-color)">FetchItemsAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">for</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">int</span> <span style="color:var(--syntax-text-color)">i</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-literal-color)">0</span><span style="color:var(--syntax-text-color)">;</span> <span style="color:var(--syntax-text-color)">i</span> <span style="color:var(--syntax-text-color)"><</span> <span style="color:var(--syntax-literal-color)">10</span><span style="color:var(--syntax-text-color)">;</span> <span style="color:var(--syntax-text-color)">i</span><span style="color:var(--syntax-text-color)">++)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-comment-color)">// Simulating async operation to retrieve data</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">Task</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">Delay</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-literal-color)">100</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">yield</span> <span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-text-color)">i</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
要使用此方法生成的项目,您可以使用以下await foreach语句异步迭代项目:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">ConsumeItemsAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-declaration-color)">foreach</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">item</span> <span style="color:var(--syntax-declaration-color)">in</span> <span style="color:var(--syntax-name-color)">FetchItemsAsync</span><span style="color:var(--syntax-text-color)">())</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">Console</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">WriteLine</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">item</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
使用IAsyncEnumerable<T>和异步流提供以下好处:
Task.Run该方法与 C# 中的异步等待模式有 什么关系,使用它来将工作卸载到 ThreadPool 时要考虑的主要因素有哪些?方法Task.Run是类提供的静态方法Task。它用于卸载要在 ThreadPool 上执行的工作,这有助于通过利用后台线程实现更好的响应能力或并行性。
的主要用例Task.Run是从主线程或 UI 线程卸载 CPU 绑定操作。使用 async-await,Task.Run您可以在后台运行这些受 CPU 限制的操作并异步等待它们完成。
下面是一个使用示例Task.Run:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-declaration-color)">long</span><span style="color:var(--syntax-text-color)">></span> <span style="color:var(--syntax-name-color)">PerformCpuBoundWorkAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">Task</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">Run</span><span style="color:var(--syntax-text-color)">(()</span> <span style="color:var(--syntax-text-color)">=></span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-comment-color)">// Perform a long-running CPU-bound operation</span>
<span style="color:var(--syntax-declaration-color)">long</span> <span style="color:var(--syntax-text-color)">result</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-name-color)">ComputeResult</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-text-color)">result</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-text-color)">});</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
使用时Task.Run,请记住以下因素:
Task.RunCPU-bound 操作或当 API 同步运行并消耗大量时间时。避免使用Task.Runfor I/O-bound 操作,async-await 模式更适合处理非阻塞并发的 I/O。Task.Run会导致 ThreadPool 饥饿并影响 UI 响应能力。在这种情况下,请考虑使用其他机制,如任务调度程序、专用工作线程或并行处理技术。Task.Run,委托中抛出的异常会在您await执行任务时传播。务必妥善处理异常,避免未观察到的异常。Task.Run调用:不要Task.Run在另一个Task.Run委托中使用。这会导致 ThreadPool 饥饿和性能下降。考虑使用更高级的模式,如Task.WhenAll、SemaphoreSlim或并行特性,Parallel.ForEach以创建更结构化的并行性。防止主线程(例如,UI 线程)阻塞的关键是在处理异步操作时完全采用异步等待模式。避免在等待异步操作时使用.Wait()或之类的同步阻塞机制,因为它们会导致死锁并阻塞主线程。.Result
为确保非阻塞行为并防止主线程等待,请遵循以下做法:
await在调用它们时使用关键字。这将允许调用方法将控制权返回给调用者,让主线程继续处理其他任务并保持响应。
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code> <span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">PerformOperationAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-comment-color)">// Correct: await the async method</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">GetResultAsync</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-comment-color)">// ...</span>
<span style="color:var(--syntax-comment-color)">// Incorrect: blocks the primary thread and can cause deadlocks</span>
<span style="color:var(--syntax-name-color)">GetResultAsync</span><span style="color:var(--syntax-text-color)">().</span><span style="color:var(--syntax-name-color)">Wait</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
await。
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code> <span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-declaration-color)">void</span> <span style="color:var(--syntax-name-color)">ButtonClickHandler</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">object</span> <span style="color:var(--syntax-text-color)">sender</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">RoutedEventArgs</span> <span style="color:var(--syntax-text-color)">e</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">PerformOperationAsync</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
Task.Run:如果您需要执行长时间运行的 CPU 绑定操作,请使用Task.Run将工作卸载到 ThreadPool 以防止阻塞主线程。
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code> <span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-declaration-color)">long</span><span style="color:var(--syntax-text-color)">></span> <span style="color:var(--syntax-name-color)">ProcessDataAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">Task</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">Run</span><span style="color:var(--syntax-text-color)">(()</span> <span style="color:var(--syntax-text-color)">=></span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-comment-color)">// Perform long-running CPU-bound operation</span>
<span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">result</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-name-color)">ComputeResult</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-text-color)">result</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-text-color)">});</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
遵循这些做法将确保主线程在等待异步操作完成时不会被阻塞,从而保持应用程序的响应能力和高性能。
此时,您已经牢牢掌握了 C# 中基本的异步等待模式和实践。下一步是深入探讨高级主题,例如使用该类进行任务调度和自定义执行TaskScheduler。
这些高级技术可以进一步增强您的异步编程技能,并帮助您处理 C# 中复杂的异步场景。
TaskScheduler,为什么在 C# 中实现高级异步场景时考虑任务的调度方式很重要?该类TaskScheduler是 C# 中任务并行库 (TPL) 的核心组件。它负责控制底层线程池如何调度和执行任务。Task.Run默认情况下,由、和其他异步方法执行的任务Task.Factory.StartNew使用默认值TaskScheduler,它在 ThreadPool 上安排任务。
在实现高级异步方案时,您可能需要考虑自定义TaskScheduler以满足特定要求或提高性能。自定义任务调度程序允许您控制任务的执行方式和时间,从而可能实现更好的资源管理、优先级排序或与其他框架、应用程序域或同步上下文的集成。
例如,您可以创建一个自定义 TaskScheduler,它在特定线程上或使用特定同步上下文来安排任务,例如 Windows 窗体应用程序中的 UI 线程:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">class</span> <span style="color:var(--syntax-name-color)">UiThreadTaskScheduler</span> <span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">TaskScheduler</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">private</span> <span style="color:var(--syntax-declaration-color)">readonly</span> <span style="color:var(--syntax-text-color)">SynchronizationContext</span> <span style="color:var(--syntax-text-color)">_synchronizationContext</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-name-color)">UiThreadTaskScheduler</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">SynchronizationContext</span> <span style="color:var(--syntax-text-color)">synchronizationContext</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">_synchronizationContext</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-text-color)">synchronizationContext</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">protected</span> <span style="color:var(--syntax-declaration-color)">override</span> <span style="color:var(--syntax-declaration-color)">void</span> <span style="color:var(--syntax-name-color)">QueueTask</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-text-color)">task</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">_synchronizationContext</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">Post</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">_</span> <span style="color:var(--syntax-text-color)">=></span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-name-color)">TryExecuteTask</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">task</span><span style="color:var(--syntax-text-color)">);</span> <span style="color:var(--syntax-text-color)">},</span> <span style="color:var(--syntax-declaration-color)">null</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">protected</span> <span style="color:var(--syntax-declaration-color)">override</span> <span style="color:var(--syntax-declaration-color)">bool</span> <span style="color:var(--syntax-name-color)">TryExecuteTaskInline</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-text-color)">task</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-declaration-color)">bool</span> <span style="color:var(--syntax-text-color)">taskWasPreviouslyQueued</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">if</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">SynchronizationContext</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Current</span> <span style="color:var(--syntax-text-color)">!=</span> <span style="color:var(--syntax-text-color)">_synchronizationContext</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-declaration-color)">false</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-name-color)">TryExecuteTask</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">task</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">protected</span> <span style="color:var(--syntax-declaration-color)">override</span> <span style="color:var(--syntax-text-color)">IEnumerable</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">Task</span><span style="color:var(--syntax-text-color)">></span> <span style="color:var(--syntax-name-color)">GetScheduledTasks</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-text-color)">Enumerable</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Empty</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">Task</span><span style="color:var(--syntax-text-color)">>();</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
自定义 时TaskScheduler,请考虑以下因素:
CancellationTokenSource和CancellationToken类在 C# 中为异步进程实现自定义取消逻辑?和类使您能够在 C# 中CancellationTokenSource为CancellationToken异步操作实现自定义取消逻辑。ACancellationTokenSource可用于生成CancellationTokens,然后将其传递给您的异步方法,使它们能够响应取消请求。
下面是使用 a 实现自定义取消逻辑的示例CancellationTokenSource:
第 1 步:创建CancellationTokenSource并获取CancellationToken:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">var</span> <span style="color:var(--syntax-text-color)">cancellationTokenSource</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-name-color)">CancellationTokenSource</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-text-color)">CancellationToken</span> <span style="color:var(--syntax-text-color)">cancellationToken</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-text-color)">cancellationTokenSource</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Token</span><span style="color:var(--syntax-text-color)">;</span>
</code></span></span>
第 2 步:将 传递CancellationToken给异步方法:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">PerformOperationAsync</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">CancellationToken</span> <span style="color:var(--syntax-text-color)">cancellationToken</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">for</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">int</span> <span style="color:var(--syntax-text-color)">i</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-literal-color)">0</span><span style="color:var(--syntax-text-color)">;</span> <span style="color:var(--syntax-text-color)">i</span> <span style="color:var(--syntax-text-color)"><</span> <span style="color:var(--syntax-literal-color)">10</span><span style="color:var(--syntax-text-color)">;</span> <span style="color:var(--syntax-text-color)">i</span><span style="color:var(--syntax-text-color)">++)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">cancellationToken</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">ThrowIfCancellationRequested</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-comment-color)">// Perform some work</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">Task</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">Delay</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-literal-color)">100</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">cancellationToken</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
第三步:调用async方法,传入CancellationToken,处理取消异常:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">InvokeOperationAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">try</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">PerformOperationAsync</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">cancellationToken</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">catch</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">OperationCanceledException</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-comment-color)">// Handle cancellation</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
第 4 步:通过调用Cancel()以下方法触发取消CancellationTokenSource:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-text-color)">cancellationTokenSource</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">Cancel</span><span style="color:var(--syntax-text-color)">();</span> <span style="color:var(--syntax-comment-color)">// Request cancellation</span>
</code></span></span>
实现自定义取消逻辑时,请考虑以下因素:
CancellationToken :始终在调用链中传递参数以在所有涉及的异步方法中启用取消。cancellationToken.ThrowIfCancellationRequested()以检查是否已请求取消并及时响应。OperationCanceledException请求取消时可能抛出的异常。CancellationTokenSource :在不再需要时妥善处理以防止资源泄漏。在设计和优化异步代码以提高性能时,解决几个常见的性能问题并应用特定技术至关重要:
ValueTask结构或缓存任务。ConfigureAwait(false)当您不需要同步上下文进行进一步的操作时(例如,在编写库代码或非 UI 代码时),请始终使用。Task.Run:用于卸载工作时要小心,因为过度使用会导致线程池拥塞并降低整体性能。主要用于Task.Run受 CPU 限制的工作,并使用适当的并行度来避免资源争用。Task.WhenAllwith AggregateExceptionhandling。SemaphoreSlim:同时运行多个异步操作时,使用、批处理或节流等机制仔细控制并发度,以避免资源耗尽、线程饥饿或性能下降。.Wait()or .Resulton async 方法来防止死锁。异步本地状态是一种在同一逻辑执行上下文中跨异步方法调用维护状态的方法,类似于ThreadLocal<T>跨线程执行维护状态的方式。该类AsyncLocal<T>用于管理 C# 中的异步本地状态,允许您存储特定于给定异步控制流的数据。
AsyncLocal<T>下面是一个用于在异步方法调用中存储值的示例:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">private</span> <span style="color:var(--syntax-declaration-color)">static</span> <span style="color:var(--syntax-declaration-color)">readonly</span> <span style="color:var(--syntax-text-color)">AsyncLocal</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-declaration-color)">int</span><span style="color:var(--syntax-text-color)">></span> <span style="color:var(--syntax-text-color)">_asyncLocalCount</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-text-color)">AsyncLocal</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-declaration-color)">int</span><span style="color:var(--syntax-text-color)">>();</span>
<span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">UpdateAsyncLocalStateAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">_asyncLocalCount</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Value</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-literal-color)">10</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">PerformOperationAsync</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">PerformOperationAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">int</span> <span style="color:var(--syntax-text-color)">currentValue</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-text-color)">_asyncLocalCount</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Value</span><span style="color:var(--syntax-text-color)">;</span> <span style="color:var(--syntax-comment-color)">// currentValue will be 10</span>
<span style="color:var(--syntax-comment-color)">// Perform some operation using the currentValue</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
将异步本地状态与异步等待结合使用时,开发人员应注意以下注意事项:
在构建复杂的应用程序时,通常需要组合多个异步操作以提供统一、内聚的工作单元。有几种策略可帮助您在 C# 应用程序中有效地组合异步操作:
Task.WhenAll并发执行多个异步操作并以聚合方式等待它们的结果。当您需要并发执行多个独立任务并一起使用它们的结果时,此策略适用。
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code> <span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">ProcessMultipleTasksAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">Task</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-declaration-color)">int</span><span style="color:var(--syntax-text-color)">></span> <span style="color:var(--syntax-text-color)">task1Result</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-name-color)">PerformOperation1Async</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-text-color)">Task</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-declaration-color)">string</span><span style="color:var(--syntax-text-color)">></span> <span style="color:var(--syntax-text-color)">task2Result</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-name-color)">PerformOperation2Async</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">Task</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">WhenAll</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">task1Result</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">task2Result</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-comment-color)">// Process the results</span>
<span style="color:var(--syntax-declaration-color)">int</span> <span style="color:var(--syntax-text-color)">result1</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-text-color)">task1Result</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Result</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">string</span> <span style="color:var(--syntax-text-color)">result2</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-text-color)">task2Result</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Result</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
Task.WhenAny当您同时运行多个操作时,用于在任务完成时有效地处理任务,并且您希望在结果到达时处理结果或等待第一个操作完成。
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code> <span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">ProcessTasksAsTheyCompleteAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">List</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-text-color)">Task</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-declaration-color)">string</span><span style="color:var(--syntax-text-color)">>></span> <span style="color:var(--syntax-text-color)">tasks</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-name-color)">GetListOfTasks</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-declaration-color)">while</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">tasks</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Count</span> <span style="color:var(--syntax-text-color)">></span> <span style="color:var(--syntax-literal-color)">0</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">Task</span><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-declaration-color)">string</span><span style="color:var(--syntax-text-color)">></span> <span style="color:var(--syntax-text-color)">completedTask</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">Task</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">WhenAny</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">tasks</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">tasks</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">Remove</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">completedTask</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">string</span> <span style="color:var(--syntax-text-color)">result</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-text-color)">completedTask</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-comment-color)">// Process the result</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
Task.ContinueWith将延续附加到任务完成时执行的任务。这允许您在仍然拥有单个组合组合单元的同时链接操作。但是,尽可能使用 async-await,因为它提供了更易读和可维护的代码结构。
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code> <span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">PerformCombinedOperationsAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-name-color)">PerformOperation1Async</span><span style="color:var(--syntax-text-color)">().</span><span style="color:var(--syntax-name-color)">ContinueWith</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">task1</span> <span style="color:var(--syntax-text-color)">=></span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">int</span> <span style="color:var(--syntax-text-color)">result1</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-text-color)">task1</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-text-color)">Result</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-comment-color)">// Process result1</span>
<span style="color:var(--syntax-comment-color)">// Execute a follow-up operation based on result1</span>
<span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-name-color)">PerformOperation2Async</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">result1</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}).</span><span style="color:var(--syntax-name-color)">Unwrap</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code> <span style="color:var(--syntax-declaration-color)">public</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">Task</span> <span style="color:var(--syntax-name-color)">PerformCombinedOperationsAsync</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">int</span> <span style="color:var(--syntax-text-color)">result1</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">PerformNestedOperationPart1Async</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-declaration-color)">string</span> <span style="color:var(--syntax-text-color)">result2</span> <span style="color:var(--syntax-text-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">PerformNestedOperationPart2Async</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">result1</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-comment-color)">// Process the combined results</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
通过使用这些策略,您可以有效地将多个异步操作组合到一个组合单元中,从而改进 C# 应用程序中的代码组织、可读性和可维护性。
总之,掌握 C# 中的异步等待概念对于构建高效且可维护的应用程序至关重要。本文涵盖了基本方面TaskScheduler,例如自定义取消逻辑、性能、异步本地状态和组合异步操作。不断探索这些主题将极大地促进您的专业发展和 C# 开发的成功。
如果你喜欢我的文章,记得关注获取更多的信息。感谢您的阅读,祝您有美好的一天!