關(guān)于C#中async/await的用法實(shí)例詳解
一直對(duì)c#中async/await的用法模模糊糊,不太清晰,今天寫了一下Demo徹底明確一下async/await的用法,以免因?yàn)閷?duì)其不了解而對(duì)后期的業(yè)務(wù)產(chǎn)生影響(比如事務(wù)導(dǎo)致的鎖表等等)。
1. 首先,async/await一般是成對(duì)出現(xiàn)才有意義。其意義在于可以等待異步操作完成后繼續(xù)順序執(zhí)行,而不是異步操作還沒(méi)處理完成主線程就進(jìn)行了下一步。
假設(shè),我們現(xiàn)在要模擬簡(jiǎn)單的下載場(chǎng)景,首先用戶點(diǎn)擊下載,那么就調(diào)用DownloadHandle方法(異步)進(jìn)行下載,然后通知用戶下載完成。其使用async/await的區(qū)別如下:
(1)使用async/await 的情況:
internal class Program
{
static void Main(string[] args)
{
DownloadHandle();
Console.ReadLine();
}
/// <summary>
/// 正常使用async/await時(shí),符合正常的業(yè)務(wù)邏輯:
/// 1. 通知用戶下載開(kāi)始
/// 2. 異步下載
/// 3. 等待異步下載完成后給用戶提示下載完成
/// </summary>
public static async void DownloadHandle()
{
Console.WriteLine("下載開(kāi)始!->主線程ID:" + Thread.CurrentThread.ManagedThreadId);
await Download();
Console.WriteLine("下載完成!->主線程ID:" + Thread.CurrentThread.ManagedThreadId);
}
/// <summary>
/// 下載
/// </summary>
/// <returns></returns>
public static Task Download()
{
return Task.Run(() =>
{
Console.WriteLine("下載線程ID:->" + Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("10%");
Console.WriteLine("30%");
Console.WriteLine("50%");
Console.WriteLine("60%");
Console.WriteLine("80%");
Console.WriteLine("99%");
Console.WriteLine("100%");
});
}
}
結(jié)果如下:

可以看到,即時(shí)下載使用了異步(線程ID不同也表明了當(dāng)前使用了異步),業(yè)務(wù)邏輯最終還是按照我們的需求,按順序正序執(zhí)行了。
(2)不使用async/await的情況:
internal class Program
{
static void Main(string[] args)
{
DownloadHandle();
Console.ReadLine();
}
/// <summary>
/// 不適用async/await時(shí),則代碼執(zhí)行順序時(shí)混亂的,不符合業(yè)務(wù)邏輯:
/// 1. 通知用戶下載開(kāi)始
/// 2. 提示下載完成
/// 3. 開(kāi)始下載
/// </summary>
public static void DownloadHandle()
{
Console.WriteLine("下載開(kāi)始!->主線程ID:" + Thread.CurrentThread.ManagedThreadId);
Download();
Console.WriteLine("下載完成!->主線程ID:" + Thread.CurrentThread.ManagedThreadId);
}
/// <summary>
/// 下載
/// </summary>
/// <returns></returns>
public static Task Download()
{
return Task.Run(() =>
{
Console.WriteLine("下載線程ID:->" + Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("10%");
Console.WriteLine("30%");
Console.WriteLine("50%");
Console.WriteLine("60%");
Console.WriteLine("80%");
Console.WriteLine("99%");
Console.WriteLine("100%");
});
}
}結(jié)果如下:

可以看到,代碼執(zhí)行順序混亂了,“下載完成” 跑到了 “下載線程ID” 前面去了,完全沒(méi)有按照我們預(yù)期的順序執(zhí)行。
2. 如果可以await的方法不進(jìn)行await,那將會(huì)怎樣呢?
(1)如果被調(diào)用的異步方法內(nèi)部使用了Task.Run,那結(jié)果可參考我們1中進(jìn)行講述的結(jié)果。開(kāi)發(fā)者可根據(jù)實(shí)際需要來(lái)進(jìn)行調(diào)用,如果異步方法的調(diào)用結(jié)果與其上下文邏輯沒(méi)有嚴(yán)格的執(zhí)行要求,則可以不進(jìn)行await(比如記錄日志等等)。反之,則需要加await。
(2)如果被調(diào)用的異步方法內(nèi)部只是返回了Task.CompletedTask,即時(shí)使用了await/async實(shí)際上還是等于同步執(zhí)行,如下圖。
internal class Program
{
static void Main(string[] args)
{
DownloadHandle();
Console.ReadLine();
}
/// <summary>
/// 模擬下載
/// </summary>
public static async void DownloadHandle()
{
Console.WriteLine("下載開(kāi)始!->主線程ID:" + Thread.CurrentThread.ManagedThreadId);
await Download();
Console.WriteLine("下載完成!->主線程ID:" + Thread.CurrentThread.ManagedThreadId);
}
/// <summary>
/// 下載
/// </summary>
/// <returns></returns>
public static Task Download()
{
Console.WriteLine("下載線程ID:->" + Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("10%");
Console.WriteLine("30%");
Console.WriteLine("50%");
Console.WriteLine("60%");
Console.WriteLine("80%");
Console.WriteLine("99%");
Console.WriteLine("100%");
return Task.CompletedTask;
}
結(jié)果如圖:

可以看到,即使DonwloadHandle方法使用了await/async,還是進(jìn)行了同步執(zhí)行,并沒(méi)有異步效果(可從所有線程ID相同看出)
3.小技巧: 異步方法的返回值類型一般都是Task或者Task<T>類型的,當(dāng)返回值為Task時(shí)(即方法的返回值類型為void),我們可以直接return Task.Run(()=>{})(以下第一段代碼),而不必awaitTask.Run(()=>{})(以下第二段代碼),這樣也可從一定程度上提高代碼執(zhí)行效率。
/// <summary>
/// 下載
/// </summary>
/// <returns></returns>
public static Task Download()
{
return Task.Run(() =>
{
Console.WriteLine("下載線程ID:->" + Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("10%");
Console.WriteLine("30%");
Console.WriteLine("50%");
Console.WriteLine("60%");
Console.WriteLine("80%");
Console.WriteLine("99%");
Console.WriteLine("100%");
});
}/// <summary>
/// 下載
/// </summary>
/// <returns></returns>
public static async Task Download()
{
await Task.Run(() =>
{
Console.WriteLine("下載線程ID:->" + Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("10%");
Console.WriteLine("30%");
Console.WriteLine("50%");
Console.WriteLine("60%");
Console.WriteLine("80%");
Console.WriteLine("99%");
Console.WriteLine("100%");
});
}到此這篇關(guān)于關(guān)于C#中async/await的用法的文章就介紹到這了,更多相關(guān)C#中async/await的用法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#利用SharpPcap實(shí)現(xiàn)網(wǎng)絡(luò)包捕獲嗅探
這篇文章主要為大家詳細(xì)介紹了C#利用SharpPcap實(shí)現(xiàn)網(wǎng)絡(luò)包捕獲嗅探,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-03-03
C#在運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建類型的實(shí)現(xiàn)方法
這篇文章主要介紹了C#在運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建類型的實(shí)現(xiàn)方法,主要通過(guò)動(dòng)態(tài)生成C#代碼再編譯成程序集來(lái)實(shí)現(xiàn)動(dòng)態(tài)創(chuàng)建類型的,需要的朋友可以參考下2014-09-09
C#使用后臺(tái)線程BackgroundWorker處理任務(wù)的總結(jié)
這篇文章主要介紹了C#使用后臺(tái)線程BackgroundWorker處理任務(wù)的總結(jié),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-07-07
C# string轉(zhuǎn)換為幾種不同編碼的Byte[]的問(wèn)題解讀
這篇文章主要介紹了C# string轉(zhuǎn)換為幾種不同編碼的Byte[]的問(wèn)題解讀,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02
分享兩種實(shí)現(xiàn)Winform程序的多語(yǔ)言支持的多種解決方案
本篇文章主要介紹了分享兩種實(shí)現(xiàn)Winform程序的多語(yǔ)言支持的多種解決方案,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧。2017-02-02

