[英].Net task scheduler behaviour
給定以下代碼,我玩了.Net任務:
public static async Task TaskSchedulerBehaviour()
{
var topLevelTasks = Enumerable.Range(0, 5).Select(async n =>
{
await Task.Delay(50); // THIS LINE MAKES THE DIFFERENCE
var steps = Enumerable.Range(0, 100000);
foreach (var batch in steps.Batch(1000)) { /* ".Batch" is contained in MoreLinq */
await Task.WhenAll(batch.Select(async step => await WorkStep(n, step)));
}
});
await Task.WhenAll(topLevelTasks);
async Task WorkStep(int worker, int step)
{
if (step % 100 == 0) {
Console.WriteLine($"worker={worker}, step={step}");
}
await Task.Delay(10);
}
}
所顯示的代碼包含一些“大型”頂級任務,它們執行大量工作(=許多小任務( WorkStep
;僅調用Task.Delay
))。
代碼中的一行標記有注釋:如果刪除此行,則可能會發生一些頂級任務排隊,直到所有其他任務都完成的情況。 如果其他“頂層”任務非常繁瑣,他們似乎會餓死。
另一方面,如果添加注釋行,則行為會好得多:似乎所有頂級任務在執行其子任務時都或多或少地具有類似的時間。 它們同時運行。
為什么會這樣? 任務調度程序不是簡單的FIFO隊列還是類似的東西?
非常感謝你
假設您正在談論線程池任務計划程序,它是許多可能的任務計划程序之一...
為什么會這樣? 任務調度程序不是簡單的FIFO隊列還是類似的東西?
有一個共享隊列通常是(非嚴格地)FIFO,另外,每個線程池線程都有自己的本地隊列,通常是(非嚴格地)LIFO 。 如果線程池線程無事可做,則可以從其他線程的本地隊列中竊取線程。
同樣,任務計划程序僅用於執行同步代碼 。 async
/ await
的概念是任務調度程序之上的抽象級別。 因此,通過添加一個await Task.Delay
,您的代碼實際上將一個概念性async
任務拆分為多個部分,每個部分都在適當的時間排隊到線程池中。 即,第一部分立即排隊; 當它運行時,它調用Task.Delay
(啟動計時器),然后Task.Delay
await
,導致該部分退出; 當計時器關閉時,第二部分立即排隊。
對於實際代碼,如Panagiotis在評論中所述,請考慮使用TPL Dataflow進行排隊。
這不是您問題的直接答案。 我提供的替代方法比直接使用任務要好得多。
您應該使用Microsoft的Reactive Framework(aka Rx)-NuGet System.Reactive
並using System.Reactive.Linq;
添加using System.Reactive.Linq;
-那么您可以執行以下操作:
public static async Task TaskSchedulerBehaviour()
{
var topLevelTasks =
from n in Observable.Range(0, 5)
from batch in Observable.Range(0, 100000).Buffer(1000)
from results in
from step in batch.ToObservable()
from result in Observable.FromAsync(() => WorkStep(n, step))
select result
select results;
await topLevelTasks.ToArray();
async Task WorkStep(int worker, int step)
{
if (step % 100 == 0)
{
Console.WriteLine($"worker={worker}, step={step}");
}
await Task.Delay(10);
}
}
Rx非常適合您處理所有調度。
您必須承認該代碼看起來也更好。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.