zoukankan      html  css  js  c++  java
  • .NET异步和多线程系列(六)- async/await

    本文是.NET异步和多线程系列的第六章,本章主要对之前介绍过的async/await做一些补充说明。

    下面我们直接来看下代码和运行结果:

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace MyAsyncAwait
    {
        /// <summary>
        /// await/async 是C#保留关键字,通常是成对出现,语法糖。
        /// 
        /// 主线程调用async/await方法,主线程遇到await返回执行后续动作,
        /// await后面的代码会等着Task任务的完成后再继续执行
        /// 其实就像把await后面的代码包装成一个ContinueWith的回调动作
        /// 然后这个回调动作可能是Task线程,也可能是新的子线程,也可能是主线程
        /// 
        /// 一个async方法,如果没有返回值,可以方法声明返回Task
        /// await/async能够用同步的方式编写代码,但又是非阻塞的。
        /// 
        /// async方法在编译后会生成一个状态机(实现了IAsyncStateMachine接口)
        /// </summary>
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine($"当前主线程开始 ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
                AwaitAsyncClass.TestShow();
                Console.WriteLine($"当前主线程结束 ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
                Console.ReadKey();
            }
        }
    
        /// <summary>
        /// await/async关键字 语法糖
        /// await/async 要么不用 要么用到底
        /// </summary>
        public class AwaitAsyncClass
        {
            public static void TestShow()
            {
                Test();
            }
    
            private async static Task Test()
            {
                Console.WriteLine($"Test开始 ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
    
                Task<long> t = SumAsync();
                Console.WriteLine($"遇到await主线程回来干活了 ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
    
                {
                    long lResult = await t;  //await后面的代码会由线程池的线程执行,非阻塞。
                    Console.WriteLine($"最终得到的lResult={lResult}");
                }
    
                {
                    //t.Wait(); //主线程等待Task的完成,阻塞的
                    //long lResult = t.Result; //访问Result,主线程等待Task的完成,阻塞的,效果跟t.Wait()一样
                    //Console.WriteLine($"最终得到的lResult={lResult}");
                }
    
                Console.WriteLine($"Test结束 ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
            }
    
            /// <summary>
            /// 带返回值的Task
            /// 要使用返回值就一定要等子线程计算完毕
            /// </summary>
            private static async Task<long> SumAsync()
            {
                Console.WriteLine($"SumAsync start ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
                long result = 0;
    
                //await后面的代码会等着Task任务的完成后再继续执行
                //其实就像把await后面的代码包装成一个ContinueWith的回调动作
                //然后这个回调动作可能是Task线程,也可能是新的子线程,也可能是主线程
                await Task.Run(() =>
                {
                    for (int k = 0; k < 2; k++)
                    {
                        Console.WriteLine($"SumAsync 第1个 await Task.Run k={k} ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
                        Thread.Sleep(1000);
                    }
                    for (long i = 0; i < 999_999_999; i++)
                    {
                        result += i;
                    }
                });
    
                Console.WriteLine($"SumAsync 第1个 await Task.Run的后续任务开始 ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
                await Task.Run(() =>
                {
                    for (int k = 0; k < 2; k++)
                    {
                        Console.WriteLine($"SumAsync 第2个 await Task.Run k={k} ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
                        Thread.Sleep(1000);
                    }
                    for (long i = 0; i < 999_999_999; i++)
                    {
                        result += i;
                    }
                });
    
                Console.WriteLine($"SumAsync 第2个 await Task.Run的后续任务开始 ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
                await Task.Run(() =>
                {
                    for (int k = 0; k < 2; k++)
                    {
                        Console.WriteLine($"SumAsync 第3个 await Task.Run k={k} ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
                        Thread.Sleep(1000);
                    }
                    for (long i = 0; i < 999_999_999; i++)
                    {
                        result += i;
                    }
                });
    
                Console.WriteLine($"SumAsync end ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
                return result;
            }
        }
    }

    运行结果如下:

    仔细观察结果会发现

      主线程调用async/await方法,主线程遇到await后会返回执行后续动作;

      await后面的代码会等着Task任务的完成后再继续执行,其实就像把await后面的代码包装成一个ContinueWith的回调动作;

      然后这个回调动作可能是Task线程,也可能是新的子线程,也可能是主线程;

      await/async能够用同步的方式编写代码,但又是非阻塞的。

    下面我们调整下Test方法后再运行(红色的为调整部分):

    private async static Task Test()
    {
        Console.WriteLine($"Test开始 ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
    
        Task<long> t = SumAsync();
        Console.WriteLine($"遇到await主线程回来干活了 ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
    
        {
            //long lResult = await t;  //await后面的代码会由线程池的线程执行,非阻塞。
            //Console.WriteLine($"最终得到的lResult={lResult}");
        }
    
        {
            //t.Wait(); //主线程等待Task的完成,阻塞的
            long lResult = t.Result; //访问Result,主线程等待Task的完成,阻塞的,效果跟t.Wait()一样
            Console.WriteLine($"最终得到的lResult={lResult}");
        }
    
        Console.WriteLine($"Test结束 ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
    }

    调整后运行结果如下:

    仔细观察结果会发现

      访问Result,主线程会等待Task的完成,阻塞的。

    其实async方法在编译后会生成一个状态机(实现了IAsyncStateMachine接口),有兴趣的可以自行去了解下。

    至此本文就介绍完了,有兴趣的还可以看下我之前写过一篇也是关于async/await的文章:https://www.cnblogs.com/xyh9039/p/11391507.html

    Demo源码:

    链接:https://pan.baidu.com/s/1jnG5IpteuKCdmF6-tr--cA 
    提取码:3onm

    此文由博主精心撰写转载请保留此原文链接:https://www.cnblogs.com/xyh9039/p/13622122.html

    版权声明:如有雷同纯属巧合,如有侵权请及时联系本人修改,谢谢!!!

  • 相关阅读:
    tips
    【十大算法实现之KNN】KNN算法实例(含测试数据和源码)
    智力趣题几则
    JAVA知多少
    R语言(入门小练习篇)
    文本分类,数据挖掘和机器学习
    推荐系统的循序进阶读物(从入门到精通)
    【贪心】PAT 1033. To Fill or Not to Fill (25)
    博弈故事一则——海盗分金币问题
    基于WordNet的英文同义词、近义词相似度评估及代码实现
  • 原文地址:https://www.cnblogs.com/xyh9039/p/13622122.html
Copyright © 2011-2022 走看看