.NET異步和多線程系列(六)- async/await
本文是.NET異步和多線程系列的第六章,本章主要對之前介紹過的async/await做一些補(bǔ)充說明。
下面我們直接來看下代碼和運(yùn)行結(jié)果:

using System;
using System.Threading;
using System.Threading.Tasks;
namespace MyAsyncAwait
{
///
/// await/async 是C#保留關(guān)鍵字,通常是成對出現(xiàn),語法糖。
///
/// 主線程調(diào)用async/await方法,主線程遇到await返回執(zhí)行后續(xù)動作,
/// await后面的代碼會等著Task任務(wù)的完成后再繼續(xù)執(zhí)行
/// 其實(shí)就像把a(bǔ)wait后面的代碼包裝成一個ContinueWith的回調(diào)動作
/// 然后這個回調(diào)動作可能是Task線程,也可能是新的子線程,也可能是主線程
///
/// 一個async方法,如果沒有返回值,可以方法聲明返回Task
/// await/async能夠用同步的方式編寫代碼,但又是非阻塞的。
///
/// async方法在編譯后會生成一個狀態(tài)機(jī)(實(shí)現(xiàn)了IAsyncStateMachine接口)
///
class Program
{
static void Main(string[] args)
{
Console.WriteLine($"當(dāng)前主線程開始 ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
AwaitAsyncClass.TestShow();
Console.WriteLine($"當(dāng)前主線程結(jié)束 ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
Console.ReadKey();
}
}
///
/// await/async關(guān)鍵字 語法糖
/// await/async 要么不用 要么用到底
///
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后面的代碼會由線程池的線程執(zhí)行,非阻塞。
Console.WriteLine($"最終得到的lResult={lResult}");
}
{
//t.Wait(); //主線程等待Task的完成,阻塞的
//long lResult = t.Result; //訪問Result,主線程等待Task的完成,阻塞的,效果跟t.Wait()一樣
//Console.WriteLine($"最終得到的lResult={lResult}");
}
Console.WriteLine($"Test結(jié)束 ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
}
///
/// 帶返回值的Task
/// 要使用返回值就一定要等子線程計算完畢
///
private static async Task<long> SumAsync()
{
Console.WriteLine($"SumAsync start ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
long result = 0;
//await后面的代碼會等著Task任務(wù)的完成后再繼續(xù)執(zhí)行
//其實(shí)就像把a(bǔ)wait后面的代碼包裝成一個ContinueWith的回調(diào)動作
//然后這個回調(diào)動作可能是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的后續(xù)任務(wù)開始 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的后續(xù)任務(wù)開始 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;
}
}
}

運(yùn)行結(jié)果如下:

仔細(xì)觀察結(jié)果會發(fā)現(xiàn):
主線程調(diào)用async/await方法,主線程遇到await后會返回執(zhí)行后續(xù)動作;
await后面的代碼會等著Task任務(wù)的完成后再繼續(xù)執(zhí)行,其實(shí)就像把a(bǔ)wait后面的代碼包裝成一個ContinueWith的回調(diào)動作;
然后這個回調(diào)動作可能是Task線程,也可能是新的子線程,也可能是主線程;
await/async能夠用同步的方式編寫代碼,但又是非阻塞的。
下面我們調(diào)整下Test方法后再運(yùn)行(紅色的為調(diào)整部分):

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后面的代碼會由線程池的線程執(zhí)行,非阻塞。
//Console.WriteLine($"最終得到的lResult={lResult}");
}
{
//t.Wait(); //主線程等待Task的完成,阻塞的
long lResult = t.Result; //訪問Result,主線程等待Task的完成,阻塞的,效果跟t.Wait()一樣
Console.WriteLine($"最終得到的lResult={lResult}");
}
Console.WriteLine($"Test結(jié)束 ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
}

調(diào)整后運(yùn)行結(jié)果如下:

仔細(xì)觀察結(jié)果會發(fā)現(xiàn):
訪問Result,主線程會等待Task的完成,阻塞的。
其實(shí)async方法在編譯后會生成一個狀態(tài)機(jī)(實(shí)現(xiàn)了IAsyncStateMachine接口),有興趣的可以自行去了解下。
至此本文就介紹完了,有興趣的還可以看下我之前寫過一篇也是關(guān)于async/await的文章:https://www.cnblogs.com/xyh9039/p/11391507.html
?
Demo源碼:
鏈接:https://pan.baidu.com/s/1jnG5IpteuKCdmF6-tr--cA
提取碼:3onm
此文由博主精心撰寫轉(zhuǎn)載請保留此原文鏈接:https://www.cnblogs.com/xyh9039/p/13622122.html
版權(quán)聲明:如有雷同純屬巧合,如有侵權(quán)請及時聯(lián)系本人修改,謝謝?。?!
版權(quán)申明:本文來源于網(wǎng)友收集或網(wǎng)友提供,如果有侵權(quán),請轉(zhuǎn)告版主或者留言,本公眾號立即刪除。
