跳转到内容

C Sharp async 与await

来自代码酷

C# async与await[编辑 | 编辑源代码]

介绍[编辑 | 编辑源代码]

asyncawait是C#中用于简化异步编程的关键字,它们使得编写异步代码更加直观和易于维护。通过使用这两个关键字,开发者可以编写出看起来像同步代码的异步代码,从而避免回调地狱(Callback Hell)并提高代码的可读性。

异步编程的核心目标是让程序在等待某些操作(如I/O操作、网络请求等)完成时,不会阻塞主线程,从而提高应用程序的响应性和吞吐量。

基本语法[编辑 | 编辑源代码]

在C#中,`async`关键字用于标记一个方法为异步方法,而`await`关键字用于等待一个异步操作的完成。异步方法通常返回`Task`或`Task<T>`。

示例1:简单的异步方法[编辑 | 编辑源代码]

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        Console.WriteLine("开始执行异步方法");
        await DoSomethingAsync();
        Console.WriteLine("异步方法执行完成");
    }

    static async Task DoSomethingAsync()
    {
        Console.WriteLine("异步操作开始");
        await Task.Delay(2000); // 模拟耗时操作
        Console.WriteLine("异步操作结束");
    }
}

输出:

开始执行异步方法
异步操作开始
(等待2秒)
异步操作结束
异步方法执行完成

解释: - `Main`方法标记为`async`,因此可以在其中使用`await`。 - `DoSomethingAsync`方法模拟了一个耗时操作(`Task.Delay`)。 - `await`会暂停当前方法的执行,直到`Task.Delay`完成,但不会阻塞主线程。

工作原理[编辑 | 编辑源代码]

当遇到`await`时,C#会将当前方法的剩余部分包装为一个回调,并在异步操作完成后继续执行。以下是`async`/`await`的工作流程:

graph TD A[调用异步方法] --> B[执行到await] B --> C[异步操作开始] C --> D[方法返回未完成的任务] D --> E[异步操作完成] E --> F[继续执行剩余代码]

返回值类型[编辑 | 编辑源代码]

异步方法可以返回以下类型: 1. `Task`:表示没有返回值的异步操作。 2. `Task<T>`:表示返回类型为`T`的异步操作。 3. `void`:通常用于事件处理程序,但不推荐在其他场景使用。

示例2:返回值的异步方法[编辑 | 编辑源代码]

static async Task<int> CalculateSumAsync(int a, int b)
{
    await Task.Delay(1000); // 模拟耗时计算
    return a + b;
}

static async Task Main(string[] args)
{
    int result = await CalculateSumAsync(3, 5);
    Console.WriteLine($"计算结果: {result}");
}

输出:

(等待1秒)
计算结果: 8

错误处理[编辑 | 编辑源代码]

异步方法可以通过`try-catch`块捕获异常,就像同步代码一样。

示例3:异步异常处理[编辑 | 编辑源代码]

static async Task ThrowExceptionAsync()
{
    await Task.Delay(1000);
    throw new InvalidOperationException("发生了错误!");
}

static async Task Main(string[] args)
{
    try
    {
        await ThrowExceptionAsync();
    }
    catch (InvalidOperationException ex)
    {
        Console.WriteLine($"捕获异常: {ex.Message}");
    }
}

输出:

(等待1秒)
捕获异常: 发生了错误!

实际应用场景[编辑 | 编辑源代码]

1. 文件I/O操作:异步读取或写入文件以避免阻塞UI线程。 2. 网络请求:如HTTP API调用。 3. 数据库操作:异步查询或更新数据库。

示例4:异步HTTP请求[编辑 | 编辑源代码]

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

class Program
{
    static async Task Main(string[] args)
    {
        using HttpClient client = new HttpClient();
        string url = "https://api.example.com/data";
        
        try
        {
            string response = await client.GetStringAsync(url);
            Console.WriteLine($"响应数据: {response}");
        }
        catch (HttpRequestException ex)
        {
            Console.WriteLine($"请求失败: {ex.Message}");
        }
    }
}

常见问题与注意事项[编辑 | 编辑源代码]

1. 避免`async void`:除非是事件处理程序,否则应使用`async Task`。 2. 死锁风险:在UI线程中错误地使用`.Result`或`.Wait()`可能导致死锁。 3. 性能开销:异步操作有轻微的性能开销,但对于I/O密集型任务利大于弊。

数学表示(可选)[编辑 | 编辑源代码]

异步操作的完成时间可以表示为: Ttotal=Tasync+Tcontinuation 其中: - Tasync是异步操作本身的耗时。 - Tcontinuation是回调执行的耗时。

总结[编辑 | 编辑源代码]

`async`和`await`是C#异步编程的核心,它们让开发者能够以同步的方式编写异步代码,同时保持代码的清晰性和可维护性。通过合理使用这两个关键字,可以显著提升应用程序的性能和用户体验。