闲来无事研究研究.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}\tIsCompleted={1}\tIsFaulted={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}\tIsCompleted={1}\tIsFaulted={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}\tIsCompleted={1}\tIsFaulted={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}\tIsCompleted={1}\tIsFaulted={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;
        }
    }
}

  

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

猜你喜欢

转载自www.cnblogs.com/Taburensheng/p/12050369.html