Condividi tramite


Panoramica della pianificazione

Due forme di pianificazione in Orleans sono rilevanti per i grani:

  1. Pianificazione delle richieste: pianificazione delle chiamate granulari in ingresso per l'esecuzione in base alle regole descritte in Pianificazione delle richieste.
  2. Pianificazione delle attività: pianificazione di blocchi sincroni di codice da eseguire in modo a thread singolo .

Tutto il codice del grano viene eseguito sul gestore di attività del grano, il che significa che anche le richieste vengono eseguite sul gestore di attività del grano. Anche se le regole di pianificazione delle richieste consentono l'esecuzione simultaneamente di più richieste, non verranno eseguite in parallelo perché il gestore delle attività del grain esegue sempre le attività una per volta e non esegue mai più attività in parallelo.

Pianificazione delle attività

Per comprendere meglio la pianificazione, prendere in considerazione il livello di granularità seguente, MyGrain. Ha un metodo denominato DelayExecution() che registra un messaggio, attende un po 'di tempo, quindi registra un altro messaggio prima di restituire.

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

Quando questo metodo viene eseguito, il corpo del metodo viene eseguito in due parti:

  1. La prima _logger.LogInformation(...) chiamata è la chiamata a Task.Delay(1_000).
  2. _logger.LogInformation(...) Seconda chiamata.

La seconda attività non è programmata nel pianificatore delle attività fino al completamento della chiamata Task.Delay(1_000). A questo punto, pianifica la continuazione del metodo del grano.

Ecco una rappresentazione grafica del modo in cui una richiesta viene pianificata ed eseguita come due attività:

Esempio di esecuzione di una richiesta basata su due attività.

La descrizione precedente non è specifica di Orleans. Descrive il funzionamento della pianificazione delle attività in .NET. Il compilatore C# converte i metodi asincroni in una macchina a stati asincrona e l'esecuzione procede attraverso questa macchina a stati in passaggi discreti. Ogni passaggio viene programmato sull'attuale TaskScheduler (accessibile tramite TaskScheduler.Current, impostazione predefinita per TaskScheduler.Default) oppure sull'attuale SynchronizationContext. Se viene utilizzato un TaskScheduler, ogni passaggio nel metodo rappresenta un'istanza Task passata a tale TaskScheduler. Pertanto, un Task in .NET può rappresentare due concetti.

  1. Operazione asincrona che può essere attesa. L'esecuzione del DelayExecution() metodo sopra è rappresentata da un Task che può essere atteso.
  2. Blocco di lavoro sincrono. Ogni fase all'interno del DelayExecution() metodo precedente è rappresentata da un oggetto Task.

Quando TaskScheduler.Default è usato, le continuazioni vengono pianificate più esplicitamente su .NET ThreadPool e non sono racchiuse in un oggetto Task. L'incapsulamento delle continuazioni nelle istanze Task avviene in modo trasparente, quindi gli sviluppatori devono raramente preoccuparsi di tali dettagli di implementazione.

Pianificazione delle attività in Orleans

Ogni attivazione granulare ha una propria TaskScheduler istanza responsabile dell'applicazione del modello di esecuzione a thread singolo di grani. Internamente, questa TaskScheduler operazione viene implementata tramite ActivationTaskScheduler e WorkItemGroup. WorkItemGroup mantiene le attività accodate in un Queue<T> (dove T è internamente un Task) e implementa IThreadPoolWorkItem. Per eseguire ogni oggetto attualmente accodato Task, WorkItemGroup pianifica se stesso in .NET ThreadPool. Quando .NET ThreadPool richiama il WorkItemGroup metodo di IThreadPoolWorkItem.Execute(), WorkItemGroup esegue le istanze accodate Task una ad una.

Ogni grano ha un pianificatore che si esegue auto-pianificandosi in .NET ThreadPool:

Orleans grani che pianificano se stessi in .NET ThreadPool.

Ogni pianificatore contiene una coda di attività:

Coda delle attività pianificate.

.NET ThreadPool esegue ogni elemento di lavoro che gli è stato accodato. Questo include schedulatori granulari e altri elementi di lavoro, come quelli pianificati tramite Task.Run(...).

Visualizzazione di tutte le utilità di pianificazione in esecuzione in .NET ThreadPool.

Annotazioni

Un gestore di attività di un grain può essere eseguito solo su un thread alla volta, ma non sempre sullo stesso thread. .NET ThreadPool è libero di utilizzare un thread diverso ogni volta che il scheduler del grain viene eseguito. Il pianificatore dei grain garantisce che venga eseguito solo su un thread alla volta, implementando il modello di esecuzione single-threaded dei grain.