Delen via


Planningsoverzicht

Er zijn twee soorten planningen Orleans die relevant zijn voor korrels:

  1. Aanvraagplanning, de planning van binnenkomende graanoproepen voor uitvoering volgens planningsregels die worden besproken in aanvraagplanning.
  2. Taakplanning, de planning van synchrone codeblokken die op één thread moeten worden uitgevoerd

Alle graancode wordt uitgevoerd op de taakplanner van het graan, wat betekent dat aanvragen ook worden uitgevoerd op de taakplanner van het graan. Zelfs als de planningsregels voor aanvragen meerdere aanvragen gelijktijdig toestaan, worden ze niet parallel uitgevoerd, omdat de taakplanner van het graan altijd taken één voor één uitvoert en daarom nooit meerdere taken parallel uitvoert.

Taken plannen

Als u meer inzicht wilt krijgen in de planning, moet u rekening houden met het volgende graan, MyGraindat een methode heeft waarmee een bericht wordt aangeroepen DelayExecution() , enige tijd wacht en vervolgens een ander bericht registreert voordat u terugkeert.

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");
    }
}

Wanneer deze methode wordt uitgevoerd, wordt de hoofdtekst van de methode in twee delen uitgevoerd:

  1. De eerste _logger.LogInformation(...) oproep en de oproep naar Task.Delay(1_000).
  2. Het tweede _logger.LogInformation(...) gesprek.

De tweede taak wordt pas gepland op de taakplanner van het graan als de Task.Delay(1_000) aanroep is voltooid. Op dat moment wordt de voortzetting van de graanmethode gepland.

Hier volgt een grafische weergave van hoe een aanvraag wordt gepland en uitgevoerd als twee taken:

Two-Task-based request execution example.

De bovenstaande beschrijving is niet specifiek Orleans voor en is hoe taakplanning in .NET werkt: asynchrone methoden in C# worden geconverteerd naar een asynchrone statusmachine door de compiler en de uitvoering wordt uitgevoerd via de asynchrone statusmachine in discrete stappen. Elke stap wordt gepland op de huidige TaskScheduler (toegankelijk via TaskScheduler.Current, standaardinstelling) TaskScheduler.Defaultof de huidige SynchronizationContext. Als een TaskScheduler wordt gebruikt, wordt elke stap in de methode vertegenwoordigd door een Task exemplaar dat wordt doorgegeven aan die TaskScheduler. Daarom kan een Task in .NET twee conceptuele dingen voorstellen:

  1. Een asynchrone bewerking waarop kan worden gewacht. De uitvoering van de DelayExecution() bovenstaande methode wordt vertegenwoordigd door een Task methode die kan worden gewacht.
  2. In een synchrone werkblok wordt elke fase binnen de DelayExecution() bovenstaande methode vertegenwoordigd door een Task.

Wanneer TaskScheduler.Default deze wordt gebruikt, worden vervolgen rechtstreeks naar het .NET ThreadPool gepland en worden ze niet verpakt in een Task object. Het verpakken van vervolgbewerkingen in Task instanties vindt transparant plaats en daarom hoeven ontwikkelaars zelden op de hoogte te zijn van deze implementatiedetails.

Taakplanning in Orleans

Elke korrelactivering heeft een eigen TaskScheduler exemplaar dat verantwoordelijk is voor het afdwingen van het uitvoeringsmodel met één thread van korrels. Intern wordt dit TaskScheduler geïmplementeerd via ActivationTaskScheduler en WorkItemGroup. WorkItemGroup houdt enqueued taken in een Queue<T> waar T is een Task intern en implementeert IThreadPoolWorkItem. Als u elke momenteel enqueued Taskwilt uitvoeren, WorkItemGroup plant u zichzelf op het .NET ThreadPool. Wanneer de .NET ThreadPool de WorkItemGroupIThreadPoolWorkItem.Execute() methode aanroept, WorkItemGroup worden de enqueued Task exemplaren één voor één uitgevoerd.

Elk graan heeft een scheduler die wordt uitgevoerd door zichzelf te plannen op het .NET ThreadPool:

Orleans grains scheduling themselves on the .NET ThreadPool.

Elke planner bevat een wachtrij met taken:

Scheduler queue of scheduled tasks.

In .NET ThreadPool wordt elk werkitem uitgevoerd dat eraan is toegewezen. Dit omvat graanplanners en andere werkitems, zoals werkitems die zijn gepland via Task.Run(...):

Visualization of the all schedulers running in the .NET ThreadPool.

Notitie

De scheduler van een graan kan slechts op één thread tegelijk worden uitgevoerd, maar wordt niet altijd uitgevoerd op dezelfde thread. Het .NET ThreadPool is gratis om een andere thread te gebruiken telkens wanneer de scheduler van de grain wordt uitgevoerd. De planner van het graan is verantwoordelijk voor het controleren of deze slechts op één thread tegelijk wordt uitgevoerd en dit is hoe het uitvoeringsmodel met één thread van korrels wordt geïmplementeerd.