问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

C# Async/Await:异步编程新趋势

创作时间:
作者:
@小白创作中心

C# Async/Await:异步编程新趋势

引用
CSDN
11
来源
1.
https://m.blog.csdn.net/2401_88677290/article/details/144167695
2.
https://m.blog.csdn.net/w_wjl1900/article/details/52822265
3.
https://m.blog.csdn.net/qq_44386034/article/details/131785757
4.
https://blog.csdn.net/m0_47472749/article/details/121632583
5.
https://zh.wikipedia.org/wiki/Async/await
6.
https://www.cnblogs.com/linghuaichong/articles/3837630.html
7.
https://okyrylchuk.dev/blog/mastering-async-and-await-in-csharp-best-practices/
8.
https://www.cnblogs.com/attilax/p/15198664.html
9.
https://learn.microsoft.com/zh-cn/dotnet/csharp/whats-new/csharp-version-history
10.
https://www.cnblogs.com/panpanwelcome/p/15826380.html
11.
https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming

在现代应用程序开发中,异步编程已成为提高程序响应性和性能的关键技术。C#语言通过引入async/await关键字,极大地简化了异步编程的复杂度,使得开发者能够编写出更简洁、高效且易于维护的代码。本文将深入探讨async/await的基本概念、应用场景及最佳实践,帮助读者更好地掌握这一重要特性。

01

异步编程的挑战与解决方案

在传统的同步编程模式下,程序会按照代码的顺序逐行执行,直到某个操作完成才会继续下一步。这种模式在处理耗时操作(如网络请求、文件读写等)时,会导致程序长时间等待,影响用户体验和系统性能。

异步编程则允许程序在等待某些操作完成的同时继续执行其他任务,从而提高应用程序的响应性和资源利用率。然而,传统的异步编程模型(如基于回调或事件的异步模式)往往会导致代码结构复杂、可读性差,给开发和维护带来困难。

为了解决这一问题,C# 5.0引入了async/await关键字,通过提供一种更接近同步编程的语法结构,使得异步代码的编写变得更加简单直观。

02

async/await的基本概念

async/await是C#中用于异步编程的一对关键字,它们共同作用于异步方法的定义和调用。

  • async:用于修饰方法,表示该方法包含异步操作,并且可以使用await关键字。当一个方法被async修饰时,它会被编译器认为是异步的,可以返回Task或Task类型的对象。

  • await:用于等待一个异步操作完成,但它不会阻塞线程,而是让控制权返回给调用者,以便执行其他工作。await关键字只能在被async修饰的方法中使用。

下面是一个简单的示例,展示了如何使用async/await来异步获取网页内容:

public async Task<string> FetchDataFromWebAsync(string url)
{
    using (HttpClient client = new HttpClient())
    {
        HttpResponseMessage response = await client.GetAsync(url);
        response.EnsureSuccessStatusCode();
        string responseBody = await response.Content.ReadAsStringAsync();
        return responseBody;
    }
}

在这个例子中,FetchDataFromWebAsync方法被async修饰,表示它是一个异步方法。方法内部使用await关键字等待client.GetAsync(url)response.Content.ReadAsStringAsync()这两个异步操作的完成。当这些操作正在进行时,控制权会返回给调用者,不会阻塞主线程。

03

async/await的应用场景

async/await特别适用于处理I/O密集型和网络密集型操作,如文件读写、数据库访问、网络请求等。通过异步执行这些耗时操作,可以显著提高应用程序的响应性和吞吐量。

网络请求

在需要从网络获取数据的场景中,使用async/await可以避免因网络延迟导致的界面卡顿。例如,从REST API获取数据:

public async Task<string> FetchDataFromApiAsync()
{
    using (HttpClient client = new HttpClient())
    {
        HttpResponseMessage response = await client.GetAsync("https://api.example.com/data");
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadAsStringAsync();
    }
}

文件读写

在处理大文件或需要频繁读写文件的场景中,使用异步I/O操作可以避免阻塞主线程。例如,异步读取文件内容:

public async Task<string> ReadFileAsync(string filePath)
{
    using (StreamReader reader = new StreamReader(filePath))
    {
        return await reader.ReadToEndAsync();
    }
}

数据库访问

在进行数据库查询时,使用异步操作可以避免因数据库响应慢导致的界面冻结。例如,使用Entity Framework Core进行异步查询:

public async Task<List<User>> GetUsersAsync()
{
    using (var context = new ApplicationDbContext())
    {
        return await context.Users.ToListAsync();
    }
}

并发执行多个任务

在需要同时执行多个异步操作的场景中,可以使用Task.WhenAllTask.WhenAny来并发执行这些任务。例如,同时从多个URL获取数据:

public async Task<List<string>> FetchDataFromMultipleUrlsAsync(List<string> urls)
{
    var tasks = urls.Select(url => FetchDataFromWebAsync(url));
    return await Task.WhenAll(tasks);
}
04

async/await的最佳实践

虽然async/await简化了异步编程,但在使用过程中仍需遵循一些最佳实践,以确保代码的正确性和性能。

全程使用async

在调用栈中全程使用async方法,避免使用阻塞调用(如.Result.Wait()),这可能导致死锁。例如:

// 不推荐
var result = GetDataAsync().Result;

// 推荐
var result = await GetDataAsync();

避免使用async void

异步方法应返回Task或Task,而不是void。使用void会使错误处理变得困难,因为调用者无法观察到任务的完成状态。例如:

// 不推荐
async void ProcessDataAsync() {}

// 推荐
async Task ProcessDataAsync() {}

避免fire-and-forget模式

未观察的异常会导致静默失败。如果需要使用fire-and-forget行为,应显式处理或记录异常。例如:

// 不推荐
ProcessDataAsync(); // 任务未等待,潜在问题被忽略

// 推荐
ProcessDataAsync()
    .ContinueWith(t => LogError(t.Exception), TaskContinuationOptions.OnlyOnFaulted);

合理使用ConfigureAwait(false)

默认情况下,await会捕获当前的同步上下文,这在UI线程中是必要的。但在库代码或非UI代码中,使用ConfigureAwait(false)可以避免不必要的上下文切换,提高性能。例如:

await ProcessDataAsync().ConfigureAwait(false);

遵循CancellationToken

在异步操作中,应尊重CancellationToken,以允许任务优雅终止。例如:

async Task GetDataAsync(CancellationToken token)
{
    await Task.Delay(1000, token);
}

谨慎使用ValueTask

ValueTask可以减少小任务的内存分配,但过度使用会增加代码复杂性。应在性能敏感的场景中谨慎使用。

05

总结

async/await作为C#中重要的异步编程特性,通过简化异步代码的编写和理解,显著提高了开发效率和代码质量。在实际开发中,合理运用async/await,遵循最佳实践,可以构建出响应性更好、性能更优的应用程序。随着异步编程在现代应用中的重要性日益凸显,掌握async/await已成为C#开发者必备的技能之一。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号