• 闲来无事研究研究.Net中的异步编程


    以前写异步操作的时候要不就是“直接定义一个Thread线程或者往线程池中扔一个委托方法”、“要不就是定一个委托变量然后通过委托的BeginInvoke和EndInvoke来实现异步操作以及异步操作结果的收集”。虽然.可跨平台的Net Core 3.0都出来了,.Net framework也干到4.7了,但是多年来写代码一直使用那两种比较传统的方式,今天闲来无事研究研究.Net 4上新加进来的任务Task,发现这东西确实不错,虽然本质上也是开一个线程出去,不过跟传统写法比起来好处确实大大的,主要总结如下几点:

     使用Task的好处:
     1、Task本身其实也是一个线程;
     2、Task可以控制线程的先后执行顺序,通过Task.WaitAll(t1, t4);可以等待指定任务执行完再继续执行其它代码;
     3、通过使用CancellationTokenSource的Cancel()结合Task方法内部的CancellationTokenSource.Token.ThrowIfCancellationRequested()可以取消某个正在运行的任务,
           如果多个任务方法中都有CancellationTokenSource.Toke.ThrowIfCancellationRequested()方法,那么还可以用一个开关一下取消多个任务;
     4、执行完的Task可以查看其执行状态,是取消、正常完成、还是异常出错(同样是动作执行完成,如果用传统代码自己往出死磕估计费很多代码量);
     5、以上几点虽然通过传统的代码逻辑也能实现,但是没有使用Task直观,需要自己实现很多逻辑控制代码;

    *使用async结合await实现异步方法的好处:
     1、比较直观,可以将传统需要写在BeginInvoke和EndInvoke的代码写在同一个方法中,中间用await隔开就行,await放到比较耗时的方法前边;
     2、async和await必须同时出现,await必须出现在有async修饰的方法中,其实await语句的下一句就是endinvoke中需要执行的代码;
     3、调用async方法时,如果主调代码是在带有async修饰的方法中,那么在调用异步方法M2时前边加上await,直接用返回类型接住结果即可;
    *4、如果调用async方法所在的代码方法外围没有async修饰符的方法中时,那么只能直接调用M2,而且需要用Task<返回类型>来接住结果,列如Task<int> w,这时候是异步调用,         如果想同步取结果,那么直接调用w.Result就可以达到阻塞的目的,当然阻塞之前是可以先执行一些其它代码的;

    废话少说,直接上代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace TestTaskAndAsync
    {
        class Program
        {
            static void Main(string[] args)
            {
                //是否执行Task测试代码
                bool isTestTask = false;
    
                if (isTestTask) //Task测试代码
                {
                    /*
                     * 使用Task的好处:
                     * 1、Task本身其实也是一个线程;
                     * 2、Task可以控制线程的先后执行顺序,通过Task.WaitAll(t1, t4);可以等待指定任务执行完再继续执行其它代码;
                     * 3、通过使用CancellationTokenSource的Cancel()结合Task方法内部的CancellationTokenSource.Toke.ThrowIfCancellationRequested()可以取消某个正在运行的任务,
                     *    如果多个任务方法中都有CancellationTokenSource.Token.ThrowIfCancellationRequested()方法,那么还可以一下取消多个任务;
                     * 4、执行完的Task可以查看其执行状态,是取消、正常完成、还是异常出错;
                     * 5、以上几点虽然通过传统的代码逻辑也能实现,但是没有使用Task直观,需要自己实现很多逻辑控制代码;
                     * 
                     * **/
                    try
                    {
                        #region 定义取消标记,使用这种标记可以结合 token.ThrowIfCancellationRequested(); 同时取消多个执行的Task
                        CancellationTokenSource tokenSource = new CancellationTokenSource();
                        var token = tokenSource.Token;
                        #endregion
    
                        #region Task t1
    
                        Task t1 = new Task(() =>
                        {
                            Console.WriteLine("Task t1 开始执行!");
                            System.Threading.Thread.Sleep(2000);
                        });
    
                        t1.ContinueWith(task =>
                        {
                            Console.WriteLine(Environment.NewLine + "Task t1执行完成,状态为: IsCanceled={0}	IsCompleted={1}	IsFaulted={2}", task.IsCanceled, task.IsCompleted, task.IsFaulted);
                            Console.WriteLine();
    
                            tokenSource.Cancel(); //触发取消操作
                        Console.WriteLine("成功在t1任务中发送取消信号!");
                        });
    
                        t1.Start();
                        #endregion
    
                        #region Task t2
    
                        Task t2 = new Task(() =>
                        {
                            try
                            {
                                Console.WriteLine("Task t2 开始执行!");
                                for (int i = 0; i < 20; i++)
                                {
                                    token.ThrowIfCancellationRequested();
                                    System.Threading.Thread.Sleep(200);
                                    Console.WriteLine($"t2打印:{i}");
                                }
                            }
                            catch (Exception ex)
                            {
                                throw ex;
                            }
    
                        }, token);
    
                        t2.ContinueWith(task =>
                        {
                            Console.WriteLine(Environment.NewLine + "Task t2执行完成,状态为: IsCanceled={0}	IsCompleted={1}	IsFaulted={2}", task.IsCanceled, task.IsCompleted, task.IsFaulted);
                            Console.WriteLine();
    
                        });
    
                        t2.Start();
    
                        #endregion
    
                        #region Task t3
                        Task t3 = new Task(() =>
                        {
                            Console.WriteLine("Task t3 开始执行!");
                            int a = 0;
                            int b = 100 / a; //如果不是Cancellation抛出的异常,那么IsFalut就是True
    
                    }, token);
    
                        t3.ContinueWith(task =>
                        {
                            Console.WriteLine(Environment.NewLine + "Task t3执行完成,状态为: IsCanceled={0}	IsCompleted={1}	IsFaulted={2}", task.IsCanceled, task.IsCompleted, task.IsFaulted);
                        });
    
                        t3.Start();
    
    
                        #endregion
    
                        #region Task t4
                        Task t4 = new Task(() =>
                        {
                            Console.WriteLine("Task t4 开始执行!");
                            for (int i = 0; i < 20; i++)
                            {
                            //token.ThrowIfCancellationRequested();
                            System.Threading.Thread.Sleep(200);
                                Console.WriteLine($"t4打印:{i}");
                            }
                        }, token);
    
                        t4.ContinueWith(task =>
                        {
                            Console.WriteLine(Environment.NewLine + "Task t4执行完成,状态为: IsCanceled={0}	IsCompleted={1}	IsFaulted={2}", task.IsCanceled, task.IsCompleted, task.IsFaulted);
                            Console.WriteLine();
    
                        });
    
                        t4.Start();
    
    
                        #endregion
    
                        Task.WaitAll(t1, t4);
    
                        Console.WriteLine(Environment.NewLine + "等待t1、t4完成后, 对Task的主调完成!");
                        Console.WriteLine();
    
                    }
                    catch (Exception ex)
                    {
                        //这里可以捕获到Task内部抛出的异常
                        Console.WriteLine($"异常:{ex.InnerException.Message}");
                    }
    
                    Console.ReadLine();
                }
                else
                {
                    /*
                     * 使用async结合await实现异步方法的好处:
                     * 1、比较直观,可以将传统需要写在BeginInvoke和EndInvoke的代码写在同一个方法中,中间用await隔开就行,await放到比较耗时的方法前边;
                     * 2、async和await必须同时出现,await必须出现在有async修饰的方法中,其实await语句的下一句就是endinvoke中需要执行的代码;
                     * 3、调用async方法时,如果主调代码是在带有async修饰的方法中,那么在调用M2时前边加上await,直接用返回类型接住结果即可;
                     * 4、如果调用async方法所在的代码方法外围没有async修饰符,那么只能直接调用需要用Task<返回类型>来接住结果,列如Task<int> w,这时候是异步调用,如果
                     * 想同步取结果,那么直接调用w.Result就可以达到阻塞的目的,当然阻塞之前是可以先执行一些其它代码的;
                     * **/
                    #region 
                    string reqm1 = Guid.NewGuid().ToString();
                    string reqm2 = Guid.NewGuid().ToString();
    
                    M1(reqm1);
    
                    Task<int> w = M2(5, 7, reqm2);
    
                    Console.WriteLine("main 主调完成!");
    
                    Console.WriteLine($"main中直调M2打印结果:{w.Result}");
    
    
                    Console.ReadLine(); 
                    #endregion
                }
    
            }
    
    
            static async void M1(string request)
            {
                Console.WriteLine($"{request}:M1开始调用M2!");
    
                int k = await M2(3,5,request);
    
                Console.WriteLine($"{request}:M1调用M2完成,返回结果为:{k}");
            }
    
            /// <summary>
            /// 
            /// </summary>
            /// <param name="a"></param>
            /// <param name="b"></param>
            /// <returns></returns>
            static async Task<int> M2(int a, int b,string request)
            {
                Console.WriteLine($"{request}:M2 开始执行,传入参数为:{a},{b}");
    
                await Task.Delay(2000);
    
                Console.WriteLine($"{request}:M2 开始计算结果,传入参数为:{a},{b}");
    
                return a + b;
            }
        }
    }
    

      

    我的学习成果分享一下,欢迎大家指正!

  • 相关阅读:
    Hbase与Maven工程的Spring配置笔记
    CentOS7.0+Hadoop2.7.2+Hbase1.2.1搭建教程
    利用Python进行博客图片压缩
    Qt下Armadillo矩阵函数库的添加
    Qt下Eigen矩阵函数库的添加
    OpenCV2.4.13+VS2013配置方法
    OpenCV2.4.13+Qt5.6.2配置方法
    异想家Win10系统安装的软件与配置
    异想家Win7系统安装的软件与配置
    STM32学习笔记:基础例子
  • 原文地址:https://www.cnblogs.com/Taburensheng/p/12050369.html
Copyright © 2020-2023  润新知