.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

版權聲明:如有雷同純屬巧合,如有侵權請及時聯系本人修改,謝謝!!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM