C#异步有多少种实现方式?

Posted 追逐时光者

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#异步有多少种实现方式?相关的知识,希望对你有一定的参考价值。

前言

  微信群里的一个提问引发的这个问题,C#异步有多少种实现方式?首先想要知道C#异步有多少中实现方式,首先我们要知道.NET提供的执行异步操作的三种模式,然后再去了解C#异步实现的方式。

.NET异步编程模式

.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)

C#异步有四种实现方式

C# 异步有多种实现方式,可归纳为以下几类:

1、异步方法(Async Method TAP模式

使用async/await关键字实现异步编程,这是比较常用的一种异步实现方式。例如:

    public async Task TestDoSomeAsync()
    
        await Task.Delay(1000);
        Console.WriteLine("Async method completed.");
    

2、任务并行库(TPL, Task Parallel Library TAP模式

通过 Task 和 Task<T> 类型实现异步编程,可以利用多核处理器,并发执行多个独立的任务。例如:

    public static async void Main(string[] args)
    
        await Task.Run(() =>
        
            Console.WriteLine("Test Task 1 completed.");
        );

        await Task.Run(() =>
        
            Console.WriteLine("Test Task 2 completed.");
        );

        // 等待所有任务完成
        Task.WaitAll();
    

3、Asynchronous Programming Model(APM模式)

是一种经典的异步编程模式,需要手动创建回调函数,用于处理完成或错误的通知。可以通过 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;
        
    

4、Event-based Asynchronous Pattern(EAP模式)

一种已过时的异步编程模式,需要使用事件来实现异步编程。例如:

需要注意的是,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/

 

 

C#异步编程

1.在系统中,每一个程序启动时,系统会分配一个进程,用以调度系统和CPU资源。而线程则是进程真正的内核。一般来说,一个进程有一个线程,但是可以通过异步编程来实现多线程编程,提高程序运行效率。


比如在程序运行中,可以使用异步编程来处理UI响应,这样程序就不必等待UI加载时间,直接进入交互逻辑运算,UI则开辟新线程来处理。实现程序的同步处理。


2. 基础的异步编程可由委托来实现。

委托实现的异步编程有三种方法,第一个是在当前线程中开辟新线程处理,并等待处理结果,再进行接下来的运算。这是最简单的异步编程,但本质上和单线程没什么区别。

第二种方法则是,开启新线程后,继续处理接下来的任务,在当前线程中定期查询异步编程是否完成,完成后再处理返回的数据结果。

第三种方法则是在开辟新线程后,完全不管新线程的进行,只是提前写好处理代码,把其委托传入新线程,系统会在新线程完成后自动回调处理方法

3. 具体代码。

第二种方法:



分析这段代码。

基础异步编程是靠委托来实现的。

每一个委托变量都有BeginInvoke和EndInvoke这两个方法。

Begin方法的作用是在系统线程池中开辟一个新的线程,计算处理这个委托变量所包含的方法。


Begin方法有三个参数,第一个参数是所包含的方法需要的参数。在这个例子中,del所委托的Add方法有两个参数,所以第一个参数就是两个整形变量。


第二和第三个参数是是回调法所需要的,等下再说。


Begin方法会返回一个实现了接口IAsyncResult的对象的引用。这个对象实现了接口的两个成员,AsyncState和IsCompleted。AsyncState是回调法用到的,Iscompleted就是这个例子中用到的变量,它是一个bool类型的值,表示新线程中的方法是否完成,当值为false的时候,就表示还没有完成。


所以循查法就是不停检测新线程是否完成,如果完成就获取返回结果,没有就继续其他代码的执行。


End方法的作用有两个,一个是返回新线程的处理结果,第二个是清除新线程的资源,节省CPU的资源。


回调法,代码如下:



在回调法中也是用委托的Begin方法来开辟新线程的,而不同点在于,回调法不需要在原来的线程中查询新线程的有关信息,而是在开辟新线程的时候,就把对结果的处理方法传进新线程,这样新线程的计算完成后,就会自动处理结果并消除线程。


传进处理方法也是通过委托实现,在Begin方法中,以第二个参数传进去。


处理方法的委托必须为AsyncResult类型的委托。AsyncResult类型的委托返回值必须为void,接受的参数必须为实现IAsyncResult的变量。Begin方法的第三个参数则是一个Object类的变量,它的作用是把处理方法需要的使用到的类传进去。比如需要把处理结果返回到主线程的一个变量中,则需要在创建新线程的时候,把这个变量传入Begin方法中。


IAsyncResult接口的另一个成员,AsyncState就是接受Begin方法传进来的那个变量。不过,一般来讲,第三个参数是把新线程的委托传进来,不然就无法在处理方法中对新线程中的处理结果进行操作了。


以上是关于C#异步有多少种实现方式?的主要内容,如果未能解决你的问题,请参考以下文章

异步编程的几种方式

C#异步编程一

C#执行异步操作的几种方式比较和总结

C#执行异步操作的几种方式比较和总结

C#执行异步操作的几种方式比较和总结

C#异步编程