Delen via


Planningsoverzicht

Twee vormen van planning in Orleans zijn van belang voor granen.

  1. Aanvraagplanning: binnenkomende graanoproepen plannen voor uitvoering volgens regels die worden besproken in aanvraagplanning.
  2. Taakplanning: synchrone codeblokken plannen 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 aanvraagplanningsregels toestaan dat meerdere aanvragen gelijktijdig worden uitgevoerd, worden ze niet parallel uitgevoerd, omdat de taakplanner van het graan altijd taken één voor één uitvoert en nooit meerdere taken parallel uitvoert.

Taakplanning

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

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 ingepland op de taakplanner van het 'grain'-systeem zodra 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:

Voorbeeld van het uitvoeren van aanvragen op basis van twee taken.

De bovenstaande beschrijving is niet specifiek voor Orleans; hierin wordt beschreven hoe taakplanning werkt in .NET. De C#-compiler converteert asynchrone methoden naar een asynchrone statusmachine en de uitvoering wordt in discrete stappen uitgevoerd via deze statusmachine. Elke stap wordt gepland op de huidige TaskScheduler (toegankelijk via TaskScheduler.Current, standaard met TaskScheduler.Default), of de huidige SynchronizationContext. Als een TaskScheduler wordt gebruikt, vertegenwoordigt elke stap in de methode een Task exemplaar dat aan die TaskSchedulerwordt doorgegeven. Daarom kan een Task in .NET twee conceptuele dingen voorstellen:

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

Wanneer TaskScheduler.Default wordt gebruikt, worden vervolgschema's rechtstreeks naar het .NET ThreadPool gepland en worden ze niet verpakt in een Task object. Het verpakken van vervolgbewerkingen in Task exemplaren vindt transparant plaats, dus ontwikkelaars hoeven zich zelden bewust 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 wachtrijtaken in een Queue<T> (waar T intern een Task) en implementeert IThreadPoolWorkItem. Om elke momenteel in de wachtrij geplaatste Task uit te voeren, plant WorkItemGroupzichzelf op .NET ThreadPool. Wanneer .NET ThreadPool de WorkItemGroup-IThreadPoolWorkItem.Execute()-methode aanroept, worden de in de wachtrij geplaatste WorkItemGroup-instanties door Task één voor één uitgevoerd.

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

Orleans deeltjes die zichzelf inroosteren op de .NET ThreadPool.

Elke planner bevat een wachtrij met taken:

Scheduler-wachtrij met geplande taken.

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

Visualisatie van de alle planners die worden uitgevoerd in .NET ThreadPool.

Opmerking

De scheduler van een graan kan slechts op één thread tegelijk worden uitgevoerd, maar wordt niet altijd uitgevoerd op dezelfde thread. Het .NET ThreadPool framework kan vrij een andere thread gebruiken telkens wanneer de scheduler van de grain wordt uitgevoerd. De scheduler van de grain zorgt ervoor dat deze slechts op één thread tegelijk wordt uitgevoerd, en implementeert het uitvoeringsmodel met één thread van korrels.