微信群里的一个提问引发的这个问题,有同学问:C#异步有多少种实现方式?想要知道C#异步有多少种实现方式,首先我们要知道.NET提供的执行异步操作的三种模式,然后再去了解C#异步实现的方式。
.NET 提供了执行异步操作的三种模式:
基于任务的异步模式 (TAP) ,该模式使用单一方法表示异步操作的开始和完成。 TAP 是在 .NET Framework 4 中引入的。 这是在 .NET 中进行异步编程的推荐方法。 C# 中的 async 和 await 关键词以及 Visual Basic 中的 Async 和 Await 运算符为 TAP 添加了语言支持。 有关详细信息,请参阅基于任务的异步模式 (TAP)。
基于事件的异步模式 (EAP),是提供异步行为的基于事件的旧模型。 这种模式需要后缀为 Async
的方法,以及一个或多个事件、事件处理程序委托类型和 EventArg
派生类型。 EAP 是在 .NET Framework 2.0 中引入的。 建议新开发中不再使用这种模式。 有关详细信息,请参阅基于事件的异步模式 (EAP)。
异步编程模型 (APM) 模式(也称为 IAsyncResult 模式),这是使用 IAsyncResult 接口提供异步行为的旧模型。 在这种模式下,同步操作需要 Begin
和 End
方法(例如,BeginWrite
和 EndWrite
以实现异步写入操作)。 不建议新的开发使用此模式。 有关详细信息,请参阅异步编程模型 (APM)。
使用async/await关键字实现异步编程,这是比较常用的一种异步实现方式。例如:
public async Task TestDoSomeAsync() { await Task.Delay(1000*10); Console.WriteLine("Async method completed."); }
通过 Task 和 Task<T> 类型实现异步编程,可以利用多核处理器,并发执行多个独立的任务。例如:
public static void TestTaskParallel() { var task1 = Task.Run(() => { Console.WriteLine("Task 1 completed."); }); var task2 = Task.Run(() => { Console.WriteLine("Task 2 completed."); }); Task<int> task3 = Task.Factory.StartNew(() => { Console.WriteLine("Task 3 completed."); return 20;// 返回一个整数值 }); //等待所有任务完成 Task.WaitAll(task1, task2, task3); }
是一种经典的异步编程模式,需要手动创建回调函数,用于处理完成或错误的通知。可以通过 IAsyncResult 设计模式的 Begin 和 End 方法来实现,其中 Begin 方法开始异步操作,而 End 方法在异步操作完成时执行,并返回异步操作的结果。
需要注意的是,APM 模式通过 IAsyncResult 接口来存储异步操作的状态和结果,相对比较复杂,代码量也较大。同时,在使用 APM 模式时,还需要手动处理回调函数和等待异步操作完成等细节工作,使得开发起来相对较为繁琐。
class Program { static void Main(string[] args) { // 创建异步操作类实例 MyAsyncClass asyncClass = new MyAsyncClass(); // 开始异步操作 IAsyncResult result = asyncClass.BeginDoWork(null, null); // 主线程执行其他操作 // 等待异步操作完成并获取结果 int res = asyncClass.EndDoWork(result); // 处理异步操作的结果 Console.WriteLine("Result: " + res); Console.ReadLine(); } } class MyAsyncClass { /// <summary> /// 异步执行的方法 /// </summary> /// <param name="callback">callback</param> /// <param name="state">state</param> /// <returns></returns> public IAsyncResult BeginDoWork(AsyncCallback callback, object state) { // 创建一个新的异步操作对象 MyAsyncResult result = new MyAsyncResult(state); // 开始异步操作 Thread thread = new Thread(() => { try { // 执行一些操作 int res = 1 + 2; // 设置异步操作的结果 result.Result = res; // 触发回调函数 callback?.Invoke(result); } catch (Exception ex) { // 设置异步操作的异常 result.Error = ex; // 触发回调函数 callback?.Invoke(result); } }); thread.Start(); // 返回异步操作对象 return result; } /// <summary> /// 结束异步执行的方法 /// </summary> /// <param name="result">result</param> /// <returns></returns> public int EndDoWork(IAsyncResult result) { // 将 IAsyncResult 转换为 MyAsyncResult 类型,并等待异步操作完成 MyAsyncResult myResult = (MyAsyncResult)result; myResult.AsyncWaitHandle.WaitOne(); // 在异步操作中抛出异常 if (myResult.Error != null) { throw myResult.Error; } // 返回异步操作的结果 return myResult.Result; } } class MyAsyncResult : IAsyncResult { public bool IsCompleted => AsyncWaitHandle.WaitOne(0); public WaitHandle AsyncWaitHandle { get; } = new ManualResetEvent(false); public object AsyncState { get; } public bool CompletedSynchronously => false; public int Result { get; set; } /// <summary> /// 存储异步操作的结果或异常信息 /// </summary> public Exception Error { get; set; } /// <summary> /// 构造函数 /// </summary> /// <param name="asyncState">asyncState</param> public MyAsyncResult(object asyncState) { AsyncState = asyncState; } }
一种已过时的异步编程模式,需要使用事件来实现异步编程。例如:
需要注意的是,EAP 模式通过事件来实现异步编程,相对于 APM 模式更加简洁易懂,同时也避免了手动处理回调函数等细节工作。但是,EAP 模式并不支持 async/await 异步关键字,因此在一些特定的场景下可能不够灵活。
public class MyAsyncClass : Component { /// <summary> /// 声明一个委托类型,用于定义异步操作的方法签名 /// </summary> /// <param name="arg"></param> /// <returns></returns> public delegate int MyAsyncDelegate(int arg); /// <summary> /// 声明一个事件,用于通知异步操作的完成 /// </summary> public event MyAsyncDelegate OperationNameCompleted; /// <summary> /// 异步执行方法,接受一个参数 arg /// </summary> /// <param name="arg"></param> public void DoWorkAsync(int arg) { // 将异步操作放入线程池中执行 ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), arg); } /// <summary> /// 真正的异步操作 /// </summary> /// <param name="obj"></param> private void DoWork(object obj) { int arg = (int)obj; int res = arg + 1; // 触发事件,传递异步操作的结果 OperationNameCompleted?.Invoke(res); } }
https://learn.microsoft.com/zh-cn/dotnet/standard/asynchronous-programming-patterns/