Planningsoverzicht
Er zijn twee soorten planningen Orleans die relevant zijn voor korrels:
- Aanvraagplanning, de planning van binnenkomende graanoproepen voor uitvoering volgens planningsregels die worden besproken in aanvraagplanning.
- 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, MyGrain
dat 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:
- De eerste
_logger.LogInformation(...)
oproep en de oproep naarTask.Delay(1_000)
. - 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:
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:
- Een asynchrone bewerking waarop kan worden gewacht. De uitvoering van de
DelayExecution()
bovenstaande methode wordt vertegenwoordigd door eenTask
methode die kan worden gewacht. - In een synchrone werkblok wordt elke fase binnen de
DelayExecution()
bovenstaande methode vertegenwoordigd door eenTask
.
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 Task
wilt uitvoeren, WorkItemGroup
plant u zichzelf op het .NET ThreadPool
. Wanneer de .NET ThreadPool
de WorkItemGroup
IThreadPoolWorkItem.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
:
Elke planner bevat een wachtrij met taken:
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(...)
:
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.