Dela via


Översikt över schemaläggning

Det finns två typer av schemaläggning Orleans som är relevanta för korn:

  1. Schemaläggning av begäranden, schemaläggning av inkommande korn kräver körning enligt schemaläggningsregler som beskrivs i Schemaläggning av begäranden.
  2. Uppgiftsschemaläggning, schemaläggning av synkrona kodblock som ska köras på ett enkelt trådat sätt

All kornkod körs på kornets schemaläggare, vilket innebär att begäranden också körs på kornets schemaläggare. Även om reglerna för schemaläggning av begäranden tillåter att flera begäranden körs samtidigt, körs de inte parallellt eftersom kornets schemaläggare alltid kör uppgifter en i taget och därför aldrig kör flera uppgifter parallellt.

Schemalägga aktivitet

För att bättre förstå schemaläggningen bör du överväga följande korn, MyGrain, som har en metod som heter DelayExecution() som loggar ett meddelande, väntar en stund och loggar sedan ett annat meddelande innan det returneras.

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

När den här metoden körs körs metodtexten i två delar:

  1. Det första _logger.LogInformation(...) anropet och anropet till Task.Delay(1_000).
  2. Det andra _logger.LogInformation(...) samtalet.

Den andra aktiviteten schemaläggs inte i kornets schemaläggare förrän anropet Task.Delay(1_000) har slutförts. Då schemaläggs fortsättningen av kornmetoden.

Här är en grafisk representation av hur en begäran schemaläggs och körs som två uppgifter:

Two-Task-based request execution example.

Beskrivningen ovan är inte specifik för Orleans och är hur aktivitetsschemaläggning i .NET fungerar: asynkrona metoder i C# konverteras till en asynkron tillståndsdator av kompilatorn och körningen förlopp via den asynkrona tillståndsdatorn i diskreta steg. Varje steg schemaläggs på den aktuella TaskScheduler (nås via TaskScheduler.Current, som standard till TaskScheduler.Default) eller den aktuella SynchronizationContext. Om en TaskScheduler används representeras varje steg i metoden av en Task instans som skickas till den TaskScheduler. Därför kan en Task i .NET representera två konceptuella saker:

  1. En asynkron åtgärd som kan väntas på. Körningen DelayExecution() av metoden ovan representeras av en Task som kan inväntas.
  2. I ett synkront arbetsblock representeras varje steg i DelayExecution() metoden ovan av en Task.

När TaskScheduler.Default används schemaläggs fortsättningar direkt till .NET ThreadPool och omsluts inte i ett Task objekt. Omslutningen av fortsättningar i Task instanser sker transparent och därför behöver utvecklare sällan vara medvetna om dessa implementeringsuppgifter.

Schemaläggning av aktiviteter i Orleans

Varje kornaktivering har en egen TaskScheduler instans som ansvarar för att framtvinga entrådad körningsmodell med korn. Internt implementeras detta TaskScheduler via ActivationTaskScheduler och WorkItemGroup. WorkItemGroup håller enqueued uppgifter i en Queue<T> där T är en Task internt och implementerar IThreadPoolWorkItem. Om du vill köra var och en som för närvarande är enqueued TaskWorkItemGroup schemalägger sig på .NET ThreadPool. När .NET ThreadPool anropar WorkItemGroupmetoden WorkItemGroup 's IThreadPoolWorkItem.Execute() kör den enqueued-instanserna Task en i taget.

Varje korn har en schemaläggare som körs genom att schemalägga sig själv på .NET ThreadPool:

Orleans grains scheduling themselves on the .NET ThreadPool.

Varje schemaläggare innehåller en kö med uppgifter:

Scheduler queue of scheduled tasks.

.NET ThreadPool kör varje arbetsobjekt som anges i den. Detta inkluderar kornschemaläggare och andra arbetsobjekt, till exempel arbetsobjekt som schemalagts via Task.Run(...):

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

Kommentar

Ett korns schemaläggare kan bara köras på en tråd i taget, men den körs inte alltid på samma tråd. .NET ThreadPool kan använda en annan tråd varje gång kornets schemaläggare körs. Kornets schemaläggare ansvarar för att se till att den bara körs på en tråd i taget och det är så den enkla trådade körningsmodellen för korn implementeras.