C#關(guān)于Task.Yeild()函數(shù)的討論
在與同事討論async/await內(nèi)部實(shí)現(xiàn)的時(shí)候,突然想到Task.Yeild()這個(gè)函數(shù),為什么呢,了解一點(diǎn)C#async/await內(nèi)部機(jī)制的都知道,在await一個(gè)異步任務(wù)(函數(shù))的時(shí)候,它會(huì)先判斷該Task是否已經(jīng)完成,如果已經(jīng)完成,則繼續(xù)執(zhí)行下去,不會(huì)返回到調(diào)用方,原因是盡量避免線程切換,因?yàn)閍wait后面部分的代碼很可能是另一個(gè)不同的線程執(zhí)行,而Task.Yeild()則可以強(qiáng)制回到調(diào)用方,或者說主動(dòng)讓出執(zhí)行權(quán),給其他Task執(zhí)行的機(jī)會(huì),可以把Task理解為協(xié)程,Task.Yeild()和Thread.sleep(0)有點(diǎn)相同。
為了證明我的結(jié)論成立,請(qǐng)看代碼:
public static async Task Test1() { await Task.CompletedTask; Thread.Sleep(1000); Console.WriteLine("Test1任務(wù)完成"); } public static async Task Test2() { await Task.Delay(1); Thread.Sleep(1000); Console.WriteLine("Test2任務(wù)完成"); } public static async Task Test3() { await Task.Yield(); Thread.Sleep(1000); Console.WriteLine("Test3任務(wù)完成"); } static void Main(string[] args) { Console.WriteLine(DateTime.Now); _ = Test1(); Console.WriteLine(DateTime.Now); Console.ReadLine(); }
按照開頭的理論,Test1()異步函數(shù)由于await了一個(gè)已經(jīng)完成的任務(wù),所以會(huì)繼續(xù)往下執(zhí)行,阻塞1秒鐘,然后回到調(diào)用方,打印的時(shí)間之差會(huì)相隔一秒。
Test2()異步函數(shù)由于await了一個(gè)未完成的任務(wù)(1ms對(duì)于CPU來說是很長的了),所以會(huì)返回調(diào)用方,然后打印相同的時(shí)間,一秒鐘之后會(huì)打印執(zhí)行完畢。
Test3()調(diào)用了Task.Yeild()函數(shù),主動(dòng)讓出執(zhí)行權(quán),所以會(huì)直接返回調(diào)用方,然后打印相同的時(shí)間,一秒之后會(huì)打印執(zhí)行完畢。
可以看到,開頭的結(jié)論是正確的。那么,有什么意義呢?Yeild的意思在這里其實(shí)就是退讓,讓出的意思,讓出什么呢?就是讓出執(zhí)行權(quán),這與Thread.sleep(0)讓出CPU執(zhí)行權(quán)給其他線程(前提是有其他線程競(jìng)爭(zhēng))有機(jī)會(huì)執(zhí)行是一個(gè)道理。
請(qǐng)看我的例子:
public static async Task OP1() { while (true) { await Task.Yield();//這里會(huì)捕捉同步上下文,由于是控制臺(tái)程序,沒有同步上下文,所以默認(rèn)的線程池任務(wù)調(diào)度器變成同步上下文 //也就是說后面的代碼將會(huì)在線程池上執(zhí)行,由于線程池工作線程數(shù)量設(shè)置為1,所以必須主動(dòng)讓出執(zhí)行權(quán),讓其他的 //任務(wù)有執(zhí)行的機(jī)會(huì) Console.WriteLine("OP1在執(zhí)行"); Thread.Sleep(1000);//模擬一些需要占用CPU的操作 } } public static async Task OP2() { while (true) { await Task.Yield(); Console.WriteLine("OP2在執(zhí)行"); Thread.Sleep(1000); } } static async Task Main(string[] args) { ThreadPool.SetMinThreads(1, 1); ThreadPool.SetMaxThreads(1, 1); //Task.Run()方法默認(rèn)使用線程池任務(wù)調(diào)度器執(zhí)行任務(wù),由于主線程不是線程池線程,所以使用Task.Run() var t = Task.Run(async () => { var t1 = OP1(); var t2 = OP2(); await Task.WhenAll(t1, t2); }); await t; Console.ReadLine(); }
可以看出OP1()和OP2()兩個(gè)協(xié)程(Task)互相爭(zhēng)用一個(gè)線程(用戶模式下的CPU),如果不主動(dòng)讓出執(zhí)行權(quán),另一個(gè)協(xié)程(Task)將不會(huì)有機(jī)會(huì)執(zhí)行。
例如:
public static async Task OP2() { while (true) { await Task.CompletedTask;//或者是直接去掉 Console.WriteLine($"OP2在執(zhí)行 {DateTime.Now}"); Thread.Sleep(1000); } }
這樣OP1()將永遠(yuǎn)不會(huì)有機(jī)會(huì)執(zhí)行。
以上就是C#中關(guān)于Task.Yeild()函數(shù)的討論的詳細(xì)內(nèi)容,更多關(guān)于C# Task.Yeild()的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#中把Datatable轉(zhuǎn)換為Json的5個(gè)代碼實(shí)例
這篇文章主要介紹了C#中把Datatable轉(zhuǎn)換為Json的5個(gè)代碼實(shí)例,需要的朋友可以參考下2014-04-04wpf實(shí)現(xiàn)超低延遲的RTMP或RTSP播放
這篇文章主要為大家詳細(xì)介紹了wpf如何實(shí)現(xiàn)超低延遲的RTMP或RTSP播放,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-04-04C#創(chuàng)建、部署、調(diào)用WebService圖文實(shí)例詳解
本文主要用詳細(xì)的圖文給大家介紹C#創(chuàng)建、部署、調(diào)用WebService的全部過程以及中間需要避免的問題。2017-11-11C#實(shí)現(xiàn)在啟動(dòng)目錄創(chuàng)建快捷方式的方法
這篇文章主要介紹了C#實(shí)現(xiàn)在啟動(dòng)目錄創(chuàng)建快捷方式的方法,涉及C#快捷方式的創(chuàng)建技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-09-09詳解C#如何在不同工作簿之間復(fù)制選定單元格區(qū)域
處理Excel文檔時(shí),我們經(jīng)常需要將數(shù)據(jù)整合到一個(gè)工作表以便于我們進(jìn)行管理或數(shù)據(jù)對(duì)比。本文將演示如何通過編程方式將選定的單元格區(qū)域從一個(gè)工作簿復(fù)制到另一個(gè)工作簿2023-02-02