C#异步处理方式
1.基于async和await关键字的异步编程
背景和基本原理
在 C# 中,async和await关键字是异步编程的核心。async用于修饰一个方法,表示这个方法是异步的,并且该方法可以包含一个或多个await表达式。await用于暂停异步方法的执行,直到等待的异步操作完成。这种方式基于任务(Task)和任务<T>(Task<T>)类型,Task表示一个异步操作,Task<T>表示一个返回值类型为T的异步操作。
例如,一个简单的异步方法读取文件内容可以这样写:
using System;
using System.IO;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
string content = await ReadFileAsync("test.txt");
Console.WriteLine(content);
}
static async Task<string> ReadFileAsync(string path)
{
using (StreamReader reader = new StreamReader(path))
{
return await reader.ReadToEndAsync();
}
}
}
在这个例子中,Main方法和ReadFileAsync方法都被标记为async。在ReadFileAsync方法中,await reader.ReadToEndAsync()暂停ReadFileAsync方法的执行,直到文件读取操作完成。当读取完成后,ReadFileAsync方法返回读取到的文件内容,然后Main方法继续执行并打印文件内容。
异常处理
当在异步方法中使用await时,异常会自然地从异步操作传播到调用者。例如,如果在ReadFileAsync方法中文件不存在,reader.ReadToEndAsync()会抛出一个异常,这个异常会在await点被重新抛出,并且可以在调用ReadFileAsync的方法(如Main方法)中使用try - catch块来捕获。
static async Task Main()
{
try
{
string content = await ReadFileAsync("nonexistent.txt");
Console.WriteLine(content);
}
catch (FileNotFoundException ex)
{
Console.WriteLine($"文件不存在: {ex.Message}");
}
}
基于Task和Task<T>类型的异步编程(不使用await)
背景和基本原理
Task和Task<T>类型提供了一种更底层的异步操作表示方式。可以通过Task.Run方法将一个同步方法包装成一个异步任务。例如,计算一个复杂的数学函数可以这样异步执行:
using System;
using System.Threading.Tasks;
class Program
{
static void Main()
{
Task<double> task = Task.Run(() => CalculateValue());
// 可以在这里执行其他操作
double result = task.Result;
Console.WriteLine(result);
}
static double CalculateValue()
{
// 模拟复杂的计算
double sum = 0;
for (int i = 0; i < 1000000; i++)
{
sum += Math.Sqrt(i);
}
return sum;
}
}
在这个例子中,Task.Run(() => CalculateValue())创建了一个异步任务,这个任务会在后台线程中执行CalculateValue方法。task.Result属性会阻塞当前线程,直到CalculateValue方法完成并返回结果。这种方式在一些简单的场景下很有用,但如果在异步操作完成之前频繁地访问task.Result,可能会导致性能问题,因为这会导致线程阻塞。
组合多个任务
可以使用Task.WhenAll和Task.WhenAny方法来组合多个任务。Task.WhenAll会等待所有给定的任务都完成,Task.WhenAny会在给定的任务中有一个完成时就返回。例如,同时下载多个文件:
using System;
using System.Net.Http;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
HttpClient client = new HttpClient();
Task<string>[] tasks = new Task<string>[3];
tasks[0] = client.GetStringAsync("https://example.com/file1.txt");
tasks[1] = client.GetStringAsync("https://example.com/file2.txt");
tasks[2] = client.GetStringAsync("https://example.com/file3.txt");
// 等待所有任务完成
Task<string[]> allTasks = Task.WhenAll(tasks);
string[] contents = await allTasks;
foreach (string content in contents)
{
Console.WriteLine(content);
}
}
}
基于事件的异步模式(EAP) - 旧的异步模式(现在不推荐使用,但在旧代码中可能会遇到)
背景和基本原理
在早期的 C# 异步编程中,事件的异步模式很常见。这种模式下,一个类会提供一个异步方法(通常以Async结尾)和一个或多个事件,用于在异步操作的不同阶段(如开始、完成、出错等)进行通知。例如,一个简单的WebClient下载文件的例子:
using System;
using System.Net;
class Program
{
static void Main()
{
WebClient client = new WebClient();
client.DownloadStringCompleted += Client_DownloadStringCompleted;
client.DownloadStringAsync(new Uri("https://example.com/file.txt"));
Console.ReadLine();
}
private static void Client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error == null)
{
Console.WriteLine(e.Result);
}
else
{
Console.WriteLine($"下载出错: {e.Error.Message}");
}
}
}
在这个例子中,WebClient的DownloadStringAsync方法用于启动异步下载操作,当下载完成(或出错)时,会触发DownloadStringCompleted事件。在事件处理程序Client_DownloadStringCompleted中,可以处理下载的结果或错误。
缺点
这种模式的主要缺点是代码的复杂性。它需要定义事件处理程序,并且在异步操作的流程控制上不如async - await模式直观。同时,它容易出现错误,如忘记订阅事件或者在事件处理程序中没有正确地处理错误情况等。另外,这种模式在组合多个异步操作时也比较复杂,相对于Task和async - await组合方式来说,代码的可读性和可维护性较差。
🏰 大屏可视化 带你体验酷炫大屏
💯 神秘个人简介 带你体验不一样得介绍
💘 为爱表白 为你那个TA,体验别致的浪漫惊喜
🎀 酷炫邀请函 带你体验高大上得邀请
亲,码字不易,动动小手,欢迎 点赞 ➕ 收藏,如 🈶 问题请 留言(私信或评论),博主看见后一定及时给您答复,💌💌💌
- 点赞
- 收藏
- 关注作者
评论(0)