Sdílet prostřednictvím


Přehled plánování

Dvě formy plánování Orleans jsou relevantní pro zrno:

  1. Plánování požadavků: Plánování spuštění příchozích "grain" volání podle pravidel probíraných v Plánování požadavků.
  2. Plánování úloh: Plánování synchronních bloků kódu, které se mají spouštět jedním vláknem

Veškerý kód odstupňovaného kódu se spouští v plánovači úloh odstupňovaného schématu, což znamená, že požadavky se provádějí také v plánovači úloh odstupňovaného schématu. I když pravidla plánování požadavků umožňují souběžné spouštění více požadavků, nebudou se spouštět současně, protože plánovač úloh grainu vždy provádí úkoly jeden po druhém a nikdy nespustí více úkolů současně.

Plánování úkolů

Pokud chcete lépe porozumět plánování, zvažte následující jednotku. MyGrain Obsahuje metodu, která zaloguje zprávu, chvíli počká a pak před návratem zaloguje další zprávu.

public interface IMyGrain : IGrain
{
    Task DelayExecution();
}

public class MyGrain : Grain, IMyGrain
{
    private readonly ILogger<MyGrain> _logger;

    public MyGrain(ILogger<MyGrain> logger) => _logger = logger;

    public async Task DelayExecution()
    {
        _logger.LogInformation("Executing first task");

        await Task.Delay(1_000);

        _logger.LogInformation("Executing second task");
    }
}

Když se tato metoda spustí, tělo metody se spustí ve dvou částech:

  1. První _logger.LogInformation(...) hovor a volání na Task.Delay(1_000).
  2. Druhý _logger.LogInformation(...) hovor.

Druhý úkol není naplánovaný v plánovači úloh pro grainy Task.Delay(1_000), dokud se volání nedokončí. V tomto okamžiku naplánuje pokračování zrnové metody.

Tady je grafické znázornění toho, jak je požadavek naplánovaný a spuštěný jako dva úkoly:

Příklad spuštění požadavku na základě dvou úloh

Výše uvedený popis není specifický Orleanspro ; popisuje, jak funguje plánování úkolů v .NET. Kompilátor jazyka C# převádí asynchronní metody na asynchronní stavový počítač a provádění prochází tímto stavem v diskrétních krocích. Každý krok je naplánován na aktuální TaskScheduler (přístupný prostřednictvím TaskScheduler.Current, výchozí hodnota TaskScheduler.Default) nebo na aktuální SynchronizationContext. Pokud je TaskScheduler použito, každý krok v metodě představuje instanci Task předanou touto TaskScheduler. Proto může v .NET Task představovat dvě koncepční věci:

  1. Asynchronní operace, na kterou lze čekat. Provedení výše uvedené metody DelayExecution() je reprezentováno Task, na který lze čekat.
  2. Synchronní blok práce Každá fáze v rámci výše uvedené DelayExecution() metody je reprezentována hodnotou Task.

Použití TaskScheduler.Default znamená, že pokračování se plánují přímo na .NET ThreadPool a nejsou zabalena do objektu Task. Zahalení pokračování v Task instancích probíhá transparentně, takže vývojáři zřídka potřebují být si vědomi těchto podrobností implementace.

Plánování úkolů v Orleans

Každá aktivace zrnitosti má vlastní TaskScheduler instanci zodpovědnou za vynucování modelu provádění s jedním vláknem zrn. Interně se to TaskScheduler implementuje prostřednictvím ActivationTaskScheduler a WorkItemGroup. WorkItemGroup udržuje zařazené úkoly v Queue<T> (kde je T interně Task) a implementuje IThreadPoolWorkItem. Chcete-li provést každou aktuálně zařazenou frontu Task, WorkItemGroup naplánuje sebe sama na rozhraní .NET ThreadPool. Když .NET ThreadPool vyvolá metodu WorkItemGroup od IThreadPoolWorkItem.Execute(), WorkItemGroup spustí zařazené instance Task jednu po druhé.

Každé vlákno má plánovač, který se vykonává naplánováním na platformě .NET ThreadPool.

Orleans zrna se plánují na platformě .NET ThreadPool.

Každý plánovač obsahuje frontu úloh:

Fronta úloh v plánovači

.NET ThreadPool spustí každou pracovní položku, která se do ní začtou. To zahrnuje plánovače zrní a také další pracovní položky, jako jsou ty, které jsou naplánovány prostřednictvím Task.Run(...):

Vizualizace všech plánovačů spuštěných v fondu vláken .NET

Poznámka:

Plánovač vlákna může běžet pouze na jednom vlákně najednou, ale nemusí vždy běžet na stejném vlákně. .NET ThreadPool může svobodně používat různá vlákna při každém spuštění plánovače zrna. Plánovač zrna zajišťuje, že se vykonává pouze na jednom vlákně a implementuje model provádění zrn s jedním vláknem.