C# Async/Await 面试题及答案

Q神日志 2023-06-12 21:40:34

您是否正在准备涉及 C# 异步编程的面试?你来对地方了!这篇内容全面的文章涵盖了广泛的 C# async-await 面试问题,这些问题将测试您对 async-await 模式的理解,以及帮助您提高技能的解决方案和示例。

从基本概念到高级技术,本文涵盖了您在技术面试中可能遇到的 C# 面试问题中的异步和等待。

在 C# async-await 中,同步上下文之间的 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,各个任务抛出的异常被包装在AggregateExceptionAggregateException处理此问题并将其解包以了解任务中发生的实际异常至关重要否则,您可能会错过关键异常或遇到意外行为。
<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,并考虑限制可用资源的并发以避免潜在问题。

在 C# async-await 中,死锁是如何发生的,在您的应用程序中有哪些有效的技术可以避免死锁?

回答

当您在异步方法上同步阻塞时,异步等待可能会发生死锁,导致调用者和被调用者相互等待,从而有效地导致死锁。

想象一下基于 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# 中实现“异步并行”的概念,使用这种方法的潜在优点或缺点是什么?

回答

异步并行是指同时执行多个异步操作,通常目的是通过在更短的总体时间内完成任务来提高性能。在 C# 中,您可以使用各种技术实现异步并行性,包括Task.WhenAllSemaphoreSlim类与多个实例结合使用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>

 

优点:

  • 性能改进:通过并发执行多个异步操作,您的应用程序可以在更短的时间内完成任务,从而提高整体性能。
  • 更好的资源利用:异步并行允许您更有效地利用系统资源,例如 CPU、I/O 和网络带宽。

缺点:

  • 复杂性:实现异步并行性可能比顺序执行更复杂,使其更难推理、调试和维护。
  • 资源使用增加:根据工作负载和并发任务的数量,异步并行可能会导致资源使用增加,如果管理不当,可能会导致资源耗尽、线程饥饿或其他性能问题。
  • 异常处理:使用异步并行处理异常可能更具挑战性,因为多个任务可以同时抛出异常。

该类如何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>

 

潜在的陷阱:

  • 同步上下文:确保在使用时避免在回调中捕获同步上下文TaskCompletionSourceConfigureAwait(false)尽可能使用)。否则,您可能会引入死锁或与同步上下文相关性相关的其他问题。
  • 异常处理:使用时TaskCompletionSource,请确保正确处理异常,方法是将它们传递给SetException方法或在回调方法中处理它们。
  • 资源泄漏TaskCompletionSource:在将非异步代码(例如,事件处理程序、计时器、网络连接)用于将其转换为异步等待代码时,请小心清理非异步代码使用的任何资源,以防止泄漏或不必要的副作用。

什么时候适合在async-await场景中应用该类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与节流、批处理或重试策略等其他机制相 结合,以确保更强大的并发解决方案。

C# 中的异步方法的异常处理是如何工作的,以及在异步等待场景中确保正确处理错误的一些常见做法是什么?

回答

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 场景下处理异常的一些常见做法:

  1. 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>

 

  1. Catch exceptions onawait:等待任务时,您可以直接在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>

 

  1. 处理多个异常:当用于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>

 

  1. 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>

 

  1. 利用异常过滤器:C# 6when在 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>

 

在C# async-await中,该方法的用途是什么Task.Yield,建议在哪些情况下使用该方法?

回答

Task.Yield方法返回一个可等待YieldAwaitable结构,当等待时,该结构会导致异步方法暂停其执行并立即将控制权交还给调用方法。这允许在异步方法等待YieldAwaitable.

Task.Yield在需要确保长时间运行或迭代异步操作不会独占当前线程或同步上下文的情况下特别有用,否则会阻塞其他操作或降低应用程序的性能。

常见用例包括Task.Yield

  • UI 响应能力:在 UI 场景中,长时间运行的计算或迭代会导致 UI 卡顿。通过在您的代码中插入一条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 结合使用时需要采取的预防措施。


async-await 如何影响 C# 中 LINQ 查询的执行,应采取哪些预防措施来防止与延迟执行相关的潜在问题?

回答

将 LINQ 与 async-await 结合使用时,请务必注意大多数标准 LINQ 运算符(例如Where、 、 等)不具有对异步Select操作GroupBy的内置支持。使用标准运算符的 LINQ 查询是同步执行的,这在异步方法中执行时可能会导致阻塞问题,尤其是在处理大型数据集时。

要使用 LINQ 处理异步处理,请考虑以下预防措施和最佳实践:

  1. 异步加载数据:在执行 LINQ 查询之前,使用 async-await 异步加载或获取您将要操作的数据。这将有助于防止阻塞 I/O 绑定操作。
<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>

 

  1. 使用异步 LINQ 库:考虑使用 System.Interactive.Async(也称为交互式扩展或 Ix.Async)或 Entity Framework Core 等库,它们提供 LINQ 运算符的异步版本。这些库允许您链接异步 LINQ 操作并为您的查询执行端到端的异步处理。
<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>

 

  1. 注意延迟执行:在使用 LINQ 和 async-await 时,请注意延迟执行的原则。LINQ 查询在枚举结果之前不会执行,这通常是异步发生的。为避免潜在问题,在对结果执行其他异步操作之前,使用异步 LINQ 库中的 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>

 

如何使用 async-await 在 C# 中实现服务器和客户端之间的双向异步通信,以及实现此通信模式的一些重要注意事项有哪些?

回答

双向异步通信是指服务端和客户端都可以异步发送和接收消息的通信模式。要在 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>

 

重要注意事项:

  • 并发性:由于客户端和服务器都可以随时发送消息,因此请准备好处理并发消息处理或决定一种消息传递协议来处理并发性。
  • 消息框架:根据您选择的通信协议,您可能需要处理消息框架,其中包括消息边界、内容编码和其他元数据。
  • 错误处理:针对连接断开、消息处理错误和协议违规等各种场景实现错误处理。
  • 安全性:在实现双向异步通信时,考虑添加身份验证、授权、加密和消息完整性验证等安全措施。

C#中的“异步流”是什么概念,如何利用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,请记住以下因素:

  • Only offload CPU-bound or blocking operations:用于Task.RunCPU-bound 操作或当 API 同步运行并消耗大量时间时。避免使用Task.Runfor I/O-bound 操作,async-await 模式更适合处理非阻塞并发的 I/O。
  • 考虑 UI 响应能力:使用 UI 应用程序时,请谨慎对待将工作卸载到 ThreadPool,因为过度使用Task.Run会导致 ThreadPool 饥饿并影响 UI 响应能力。在这种情况下,请考虑使用其他机制,如任务调度程序、专用工作线程或并行处理技术。
  • 正确处理异常:使用时Task.Run,委托中抛出的异常会在您await执行任务时传播。务必妥善处理异常,避免未观察到的异常。
  • 避免嵌套Task.Run调用:不要Task.Run在另一个Task.Run委托中使用。这会导致 ThreadPool 饥饿和性能下降。考虑使用更高级的模式,如Task.WhenAllSemaphoreSlim或并行特性,Parallel.ForEach以创建更结构化的并行性。

在 C# async-await 中,如何在不求助于忙等待或自旋的情况下防止主线程在等待异步操作结果时阻塞?

回答

防止主线程(例如,UI 线程)阻塞的关键是在处理异步操作时完全采用异步等待模式。避免在等待异步操作时使用.Wait()或之类的同步阻塞机制,因为它们会导致死锁并阻塞主线程。.Result

为确保非阻塞行为并防止主线程等待,请遵循以下做法:

  1. 使用异步方法并等待它们的结果:总是喜欢异步方法并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>

 

  1. 一直异步:在整个调用堆栈中一致地应用异步等待模式。这通常意味着更改您的事件处理程序、委托或回调以使用异步方法和关键字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>

 

  1. 使用卸载 CPU 绑定工作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# 中复杂的异步场景。


类在 async-await 中的作用是什么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,请考虑以下因素:

  • 兼容性:确保您的自定义调度程序与应用程序中使用的特定异步等待场景和 API 兼容。
  • 性能:测试和优化自定义调度程序的性能,因为它可能会对异步操作的整体性能产生重大影响。
  • 错误处理:在您的自定义调度程序中实施适当的异常处理,确保传播异常并在必要时妥善处理它们。
  • 集成:如果您的自定义调度程序需要使用特定的同步上下文或其他框架,则必须实现无缝集成并避免潜在的死锁、性能问题或同步问题。

如何使用CancellationTokenSourceCancellationToken类在 C# 中为异步进程实现自定义取消逻辑?

回答

和类使您能够在 C# 中CancellationTokenSourceCancellationToken异步操作实现自定义取消逻辑。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>

 

实现自定义取消逻辑时,请考虑以下因素:

  • 传递 CancellationTokenCancellationToken :始终在调用链中传递参数以在所有涉及的异步方法中启用取消。
  • 经常检查取消:经常在您的异步方法中使用cancellationToken.ThrowIfCancellationRequested()以检查是否已请求取消并及时响应。
  • Handle OperationCanceledException:确保处理OperationCanceledException请求取消时可能抛出的异常。
  • Dispose CancellationTokenSourceCancellationTokenSource :在不再需要时妥善处理以防止资源泄漏。

在 C# async-await 中,有哪些常见的性能问题和优化技术来确保异步代码在生产环境中高效运行?

回答

在设计和优化异步代码以提高性能时,解决几个常见的性能问题并应用特定技术至关重要:

  1. 最小化分配和开销:避免创建不必要的任务,尤其是在循环中使用异步方法或构建大量短期任务时。这可能会导致显着的开销、增加的内存使用量和增加的 GC 压力。如果可能,请考虑使用ValueTask结构或缓存任务。
  2. 优化同步上下文:默认情况下,异步方法在启动时会捕获同步上下文,这在某些情况下可能会导致性能问题。ConfigureAwait(false)当您不需要同步上下文进行进一步的操作时(例如,在编写库代码或非 UI 代码时),请始终使用。
  3. Task.Run 的最佳使用Task.Run:用于卸载工作时要小心,因为过度使用会导致线程池拥塞并降低整体性能。主要用于Task.Run受 CPU 限制的工作,并使用适当的并行度来避免资源争用。
  4. 确保正确的异常处理:异步代码中未处理的异常可能导致意外的应用程序故障或未观察到的任务异常。确保在异步方法中正确捕获和处理异常,并使用适当的错误处理模式,例如使用Task.WhenAllwith AggregateExceptionhandling。
  5. 控制并发性SemaphoreSlim:同时运行多个异步操作时,使用、批处理或节流等机制仔细控制并发度,以避免资源耗尽、线程饥饿或性能下降。
  6. 避免死锁:死锁可能是异步代码中的一个重要性能问题,尤其是在将异步等待与同步代码混合使用或错误使用阻塞机制时。始终始终如一地使用 async-await 并避免使用.Wait()or .Resulton async 方法来防止死锁。
  7. 测量和分析性能:使用性能分析和诊断工具(如 Visual Studio Diagnostics 或 PerfView)来监视和优化异步代码的性能。确保您的代码满足您的性能预期,并主动解决潜在的瓶颈或问题。

async-local 状态在 C# 中如何工作,开发人员在将此功能与 async-await 结合使用以跨异步调用维护状态时应注意什么?

回答

异步本地状态是一种在同一逻辑执行上下文中跨异步方法调用维护状态的方法,类似于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# 应用程序中使用 async-await 将多个异步操作有效组合成单个组合单元的策略有哪些?

回答

在构建复杂的应用程序时,通常需要组合多个异步操作以提供统一、内聚的工作单元。有几种策略可帮助您在 C# 应用程序中有效地组合异步操作:

  1. Task.WhenAll:用于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>

 

  1. Task.WhenAnyTask.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>

 

  1. Task.ContinueWith:用于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>

 

  1. 嵌套异步方法:组合多个异步操作时,您还可以创建执行部分组合的嵌套异步方法,从而更轻松地管理应用程序逻辑的复杂性和可维护性。
<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# 开发的成功。

 

 


如果你喜欢我的文章,记得关注获取更多的信息。感谢您的阅读,祝您有美好的一天!
 

...全文
439 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

6

社区成员

发帖
与我相关
我的任务
社区描述
分享
java-rocketmqpygame前端 个人社区 广东省·广州市
社区管理员
  • Q shen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧