.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

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

posted @ 2020-09-06 18:59  谢友海  阅读(1089)  评论(0编辑  收藏  举报