簡體   English   中英

.Net任務計划程序行為

[英].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.Reactiveusing 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM