1. 程式人生 > 程式設計 >C#關於Task.Yeild()函式的討論

C#關於Task.Yeild()函式的討論

在與同事討論async/await內部實現的時候,突然想到Task.Yeild()這個函式,為什麼呢,瞭解一點C#async/await內部機制的都知道,在await一個非同步任務(函式)的時候,它會先判斷該Task是否已經完成,如果已經完成,則繼續執行下去,不會返回到呼叫方,原因是儘量避免執行緒切換,因為await後面部分的程式碼很可能是另一個不同的執行緒執行,而Task.Yeild()則可以強制回到呼叫方,或者說主動讓出執行權,給其他Task執行的機會,可以把Task理解為協程,Task.Yeild()和Thread.sleep(0)有點相同。

為了證明我的結論成立,請看程式碼:

public static async Task Test1()
{
   await Task.CompletedTask;
   Thread.Sleep(1000);
   Console.WriteLine("Test1任務完成");
}
public static async Task Test2()
{
   await Task.Delay(1);
   Thread.Sleep(1000);
   Console.WriteLine("Test2任務完成");
}
public static async Task Test3()
{
   await Task.Yield();
   Thread.Sleep(1000);
   Console.WriteLine("Test3任務完成");
}
static void Main(string[] args)
{
   Console.WriteLine(DateTime.Now);
   _ = Test1();
   Console.WriteLine(DateTime.Now);
   Console.ReadLine();
}

按照開頭的理論,Test1()非同步函式由於await了一個已經完成的任務,所以會繼續往下執行,阻塞1秒鐘,然後回到呼叫方,列印的時間之差會相隔一秒。

C#關於Task.Yeild()函式的討論

Test2()非同步函式由於await了一個未完成的任務(1ms對於CPU來說是很長的了),所以會返回呼叫方,然後列印相同的時間,一秒鐘之後會列印執行完畢。

C#關於Task.Yeild()函式的討論

Test3()呼叫了Task.Yeild()函式,主動讓出執行權,所以會直接返回呼叫方,然後列印相同的時間,一秒之後會列印執行完畢。

C#關於Task.Yeild()函式的討論

可以看到,開頭的結論是正確的。那麼,有什麼意義呢?Yeild的意思在這裡其實就是退讓,讓出的意思,讓出什麼呢?就是讓出執行權,這與Thread.sleep(0)讓出CPU執行權給其他執行緒(前提是有其他執行緒競爭)有機會執行是一個道理。

請看我的例子:

public static async Task OP1()
{
   while (true)
   {
     await Task.Yield();//這裡會捕捉同步上下文,由於是控制檯程式,沒有同步上下文,所以預設的執行緒池任務排程器變成同步上下文
                   //也就是說後面的程式碼將會線上程池上執行,由於執行緒池工作執行緒數量設定為1,所以必須主動讓出執行權,讓其他的
                   //任務有執行的機會
     Console.WriteLine("OP1在執行");
     Thread.Sleep(1000);//模擬一些需要佔用CPU的操作
   }
}
public static async Task OP2()
{
   while (true)
   {
     await Task.Yield();
     Console.WriteLine("OP2在執行");
     Thread.Sleep(1000);
   }
}
static async Task Main(string[] args)
{
   ThreadPool.SetMinThreads(1,1);
   ThreadPool.SetMaxThreads(1,1);
   //Task.Run()方法預設使用執行緒池任務排程器執行任務,由於主執行緒不是執行緒池執行緒,所以使用Task.Run()
   var t = Task.Run(async () =>
   {
     var t1 = OP1();
     var t2 = OP2();
     await Task.WhenAll(t1,t2);
   });
   await t;
   Console.ReadLine();
}

C#關於Task.Yeild()函式的討論

可以看出OP1()和OP2()兩個協程(Task)互相爭用一個執行緒(使用者模式下的CPU),如果不主動讓出執行權,另一個協程(Task)將不會有機會執行。

例如:

public static async Task OP2()
{
   while (true)
   {
     await Task.CompletedTask;//或者是直接去掉
     Console.WriteLine($"OP2在執行 {DateTime.Now}");
     Thread.Sleep(1000);
   }
}

這樣OP1()將永遠不會有機會執行。

C#關於Task.Yeild()函式的討論

以上就是C#中關於Task.Yeild()函式的討論的詳細內容,更多關於C# Task.Yeild()的資料請關注我們其它相關文章!