首页 > 编程语言 >C#异步有多少种实现方式?

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

时间:2024-03-17 14:30:25浏览次数:21  
标签:异步 Task C# 模式 public result 操作 多少

前言

  微信群里的一个提问引发的这个问题,有同学问: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#异步四种实现方式

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

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


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

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

通过 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);
        }

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);
        }
    }

使用总结   

       综合上面三种执行异步操作的模式而言,得出以下结论:TAP适合简单的并行场景,EAP更适合清晰的控制有来有往的多端异步场景,APM更适合控制求解复杂结构的返回值的场景。各有各种方便的地方,也各有表达困难的地方,有的方法看着麻烦,但是思路清晰,便于调试。有的办法看似简单,结合交互通信就让人费解。不是说新的就一定好,合适的方法才是最好的。

参考文章

https://learn.microsoft.com/zh-cn/dotnet/standard/asynchronous-programming-patterns/

标签:异步,Task,C#,模式,public,result,操作,多少
From: https://blog.csdn.net/qq_37237487/article/details/136620760

相关文章

  • 【c语言练习之二分查找】
    二分查找二分查找的前提二分查找必须是在一个整形的有序数组中实现二分查找的思想对于一个整形的有序数组,输入一个你想要查找的数key,将key与数组的中间元素mid作比较,使得数组被分成2部分,要查找的数key肯定在某一部分,这样就可以舍弃另一部分,在另一部分中继续用这种思......
  • C#下推荐的第三方库,用于处理网络重连逻辑
    在C#中,处理网络重连逻辑的第三方库可以大大简化开发过程,并提高应用程序的健壮性。以下是一些推荐的第三方库,以及如何在C#中使用它们来实现网络重连逻辑。1.PollyPolly是一个流行的.NET库,用于异常处理和重试逻辑。它支持丰富的重试策略,包括固定延迟、指数退避、令牌桶等......
  • DVWA靶场 之Command Injection。
    说明:这个靶场在虚拟机中,用主机通过靶场的漏洞远程连接虚拟机。命令拼接:|:管道符,直接执行后面的语句。||:前面的语句出错时才执行后面的语句。&:无论前面的语句是真是假都会执行后面的语句。&&:前面的语句为真才执行后面的语句。DVWASecurity:Low创建用户c:127.0.0.1&netuse......
  • lc2334 元素值大于变化阈值的子数组
    给定数组nums[n]和整数threshold,找到长度为k的子数组,满足子数组中每个元素都大于threshold/k,返回满足条件的任意一个k即可,如不存在,返回-1。1<=n<=1e5;1<=nums[i],threshold<=1e9子数组每个元素都大于t,也就是最小值大于t。对于固定的最小值,显然子数组越长越有可能满足条件,因此考......
  • call仿函数
    call仿函数####__call__魔术方法''' 触发时机:把对象当作函数调用的时候自动触发 功能:模拟函数化操作 参数:参数不固定,至少一个self参数 返回值:看需求'''#(1)基本语法classMyClass(): def__call__(self): print("__call__魔术方法被触发...")obj=M......
  • C语言中的置0和置1操作
    C语言中的置0和置1操作置0如果要将一个数的6位和10位置0,生成一个第6位和第10位都为0的数,拿这个数与要操作的数做(&)操作即可。生成一个第6位和第10位都为0的数可以使用1左移6位或者10位取反得到。//test=0b0000010001000000unsignedshorttest=1<<6|1<<10;/......
  • lc907 子数组的最小值之和
    给定数组arr[n],求所有子数组中最小值的和,答案对1e9+7取模。1<=n<=30000;1<=arr[i]<=30000考虑每个数作为最小值对应的子数组有多少个,计算对答案的贡献,而子数组的个数可以用单调栈来维护。数组元素可能相同,为了避免重复计数,用半开半闭区间。classSolution{public:ints......
  • Excel查找两列数据相同的元素
    =IF(ISERROR(MATCH(A1,$C$1:$C$5,0)),"",A1)//没找到返回空值,否则返回本身A1---单个数据$C$1:$C$5---数据堆在数据堆中查找是否有单个数据""---没有返回空白,此处可以修改为A1,A1---有返回本身,此处可以修改为空白----------------------------------------------------------......
  • Codeforces Round 934 (Div. 2)
    CodeforcesRound934(Div.2)A-DestroyingBridges解题思路:完全图每个点的连边数为\(n-1\)。\(k<n-1\):都可到达。\(k\geqn-1\):将点\(1\)的边删完,只能呆在点\(1\)。代码:#include<bits/stdc++.h>usingnamespacestd;usingll=longlong;usingpii=......
  • Clique Partition
    哎,就差一个考虑上下界啊!来看看官解首先一个连通块的大小不可能超过\(k\),比较显然当\(n>k\)的时候,我们将点连续的分成\(\lceil\frac{n}{k}\rceil\)个,然后考虑\(n=k\)的情形官解是这么分权值的其实我考试的时候想出来这个的,手搓几次样例就可以发现了。。但是我却没有利用上......