C# 异步编程

栏目总目录


异步编程

asyncawait 关键字是 C# 5.0 引入的两个非常重要的关键字,它们一起工作,使得异步编程变得简单和直观。

async 关键字

  • async 关键字用于标记一个方法、lambda 表达式、匿名方法或局部方法作为异步方法。这告诉编译器该方法内部可以使用 await 关键字。
  • 异步方法会隐式返回一个 TaskTask 对象。如果方法没有返回值(即返回类型为 void),则它应该用于事件处理程序,并应该避免在库或框架代码中使用,因为 void 返回类型的方法无法等待或捕获异常。

await 关键字

  • await 关键字用于等待异步操作的完成。它只能用在被 async 修饰的方法内部。
  • 当编译器看到 await 表达式时,它会将方法的其余部分安排在 await 表达式表示的异步操作完成后继续执行。在等待期间,控制权会返回给方法的调用者,允许调用者继续执行其他操作,而不是阻塞等待异步操作的完成。
  • await 表达式的结果是异步操作的结果。如果操作返回 Task,则 await 表达式的类型是 T。如果操作返回 Task,则 await 表达式通常用于仅等待操作完成,而不获取其结果。

深入async和await

async和await做了什么

示例

下面是一个简单的异步方法示例,该方法使用 HttpClient 异步获取网页的内容:

using System;
using System.Net.Http;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        string content = await GetWebPageAsync("https://www.example.com");
        Console.WriteLine(content);
    }

    static async Task<string> GetWebPageAsync(string url)
    {
        using (HttpClient client = new HttpClient())
        {
            HttpResponseMessage response = await client.GetAsync(url);
            if (response.IsSuccessStatusCode)
            {
                return await response.Content.ReadAsStringAsync();
            }
            else
            {
                throw new HttpRequestException($"Status code does not indicate success: {response.StatusCode}");
            }
        }
    }
}

在这个示例中,Main 方法被标记为 async,这允许它内部使用 awaitGetWebPageAsync 方法是另一个异步方法,它使用 HttpClientGetAsync 方法来异步获取网页,并使用 await 等待操作完成。然后,它读取响应内容并返回。注意,异常处理也是异步编程中的一个重要方面,上述示例展示了如何在异步方法中抛出和捕获异常。


高效异步

在.NET Core(或现在更常见的.NET 5/6/7等)中实现高效的异步操作主要依赖于理解异步编程模式,特别是如何正确使用asyncawait关键字,以及理解异步操作背后的线程和任务的管理。以下是一些实现高效异步操作的关键点:

1. 理解asyncawait

  • 使用async修饰方法:这表示该方法是异步的,并允许在方法内部使用await
  • 使用await等待异步操作:这会导致当前方法的执行暂停,并在异步操作完成时恢复。重要的是要等待那些真正需要等待的操作,避免不必要的await调用,因为这会增加延迟。

2. 避免阻塞调用

  • 在异步方法中,应避免使用.Result.Wait()来等待另一个异步操作的结果,因为这会导致死锁,并破坏异步方法的优势。
  • 使用await来等待异步操作完成,并让线程池来管理线程。

3. 并行与并发

  • 并行处理:使用Task.WhenAll来并行执行多个不相互依赖的异步操作,这可以显著提高性能,特别是当这些操作是I/O密集型时。
  • 并发控制:在需要时使用SemaphoreSlimasync锁(如Mutex的异步版本)来控制对共享资源的并发访问。

4. 异步资源管理

  • 使用using语句或async版本的IDisposable接口(如果可用)来确保异步操作中的资源被正确释放。
  • 注意,在async方法中,using语句仍然有效,但Dispose方法将在等待的异步操作完成后调用。

5. 避免不必要的异步

  • 如果一个操作本身很快,并且是CPU密集型的,那么将其包装为异步可能不会带来性能上的好处,反而可能因为额外的开销(如任务调度和上下文切换)而降低性能。

6. 性能测试与调优

  • 使用性能测试工具(如BenchmarkDotNet)来测量和比较不同异步实现的性能。
  • 根据测试结果进行调优,比如优化数据访问模式、减少不必要的异步调用或改进算法。

7. 使用合适的异步API

  • 尽量使用.NET Core提供的异步API,如HttpClientFileStream的异步方法等,这些API已经过优化,可以提供更好的性能。
  • 如果需要,可以自己实现异步方法,但要确保正确管理线程和任务。

8. 监控和日志记录

  • 在生产环境中监控异步操作的性能,包括响应时间、吞吐量等关键指标。
  • 使用日志记录来跟踪异步操作的执行流程,以便在出现问题时进行调试和故障排查。

你可能感兴趣的:(C#,重点,c#,开发语言)