Compartilhar via


Visão geral do agendamento

Há duas formas de agendamento no Orleans que são relevantes para a granularidade:

  1. Agendamento de solicitação, o agendamento de chamadas de granularidade de entrada para execução de acordo com as regras de agendamento discutidas no agendamento da Solicitação.
  2. Agendamento de tarefas, o agendamento de blocos síncronos de código a serem executados em uma maneira de thread única

Todo o código de granularidade é executado no agendador de tarefas de granularidade, o que significa que as solicitações também são executadas no agendador de tarefas da granularidade. Mesmo que as regras de agendamento de solicitação permitam que várias solicitações sejam executadas simultaneamente, elas não serão executadas em paralelo porque o agendador de tarefas de granularidade sempre executa tarefas um por um e, portanto, nunca executa várias tarefas em paralelo.

Agendamento de tarefas

Para reconhecer melhor o agendamento, considere a seguinte granularidade, MyGrain, que tem um método chamado DelayExecution() que registra uma mensagem, aguarda algum tempo e registra outra mensagem antes de retornar.

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 esse método for executado, o corpo do método será executado em duas partes:

  1. A primeira chamada _logger.LogInformation(...) e a chamada para Task.Delay(1_000).
  2. A segunda chamada _logger.LogInformation(...).

A segunda tarefa não será agendada no agendador de tarefas de granularidade até que a chamada Task.Delay(1_000) seja concluída, momento em que agendará a continuação do método de granularidade.

Aqui temos uma representação gráfica de como uma solicitação é agendada e executada como duas tarefas:

Two-Task-based request execution example.

A descrição acima não é específica do Orleans e é como o agendamento de tarefas no .NET funciona: métodos assíncronos em C# são convertidos em uma máquina de estado assíncrono pelo compilador, e a execução progride pela máquina de estado assíncrono em etapas discretas. Cada etapa é agendada no TaskScheduler atual (acessado por meio TaskScheduler.Current, padronizando para TaskScheduler.Default) ou SynchronizationContext atual. Se um TaskScheduler estiver sendo usado, cada etapa no método será representada por uma instância Task que é passada para aquele TaskScheduler. Portanto, um Task no .NET pode representar duas coisas conceituais:

  1. Uma operação assíncrona que pode ser aguardada. A execução do método DelayExecution() acima é representada por um Task que pode ser aguardado.
  2. Em um bloco síncrono de trabalho, cada estágio dentro do método DelayExecution() acima é representado por um Task.

Quando TaskScheduler.Default estiver em uso, as continuações são agendadas diretamente no .NET ThreadPool e não são encapsuladas em um objeto Task. O encapsulamento de continuações em instâncias Task ocorre de forma transparente e, portanto, os desenvolvedores raramente precisam estar cientes desses detalhes de implementação.

Agendamento de tarefas no Orleans

Cada ativação de granularidade tem sua própria instância TaskScheduler, que é responsável por impor o modelo de execução de granularidade de thread único. Internamente, este TaskScheduler é implementado por meio de ActivationTaskScheduler e WorkItemGroup. WorkItemGroup mantém tarefas enfileiradas em um Queue<T> onde T é um Task internamente e implementa IThreadPoolWorkItem. Para executar cada Task atualmente enfileirado, WorkItemGroup agenda a si mesmo no .NET ThreadPool. Quando o .NET ThreadPool invoca o método IThreadPoolWorkItem.Execute() de WorkItemGroup, o WorkItemGroup executa as instâncias Task enfileiradas um por um.

Cada granularidade tem um agendador que é executado agendando-se no .NET ThreadPool:

Orleans grains scheduling themselves on the .NET ThreadPool.

Cada agendador contém uma fila de tarefas:

Scheduler queue of scheduled tasks.

O .NET ThreadPool executa cada item de trabalho enfileirado nele. Isso inclui agendadores de granularidade, bem como outros itens de trabalho, como itens de trabalho agendados por meio de Task.Run(...):

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

Observação

O agendador de uma granularidade só pode ser executado em um thread por vez, mas nem sempre é executado no mesmo thread. O .NET ThreadPool é livre para usar um thread diferente sempre que o agendador do granularidade for executado. O agendador de granularidade é responsável por garantir que ele seja executado apenas em um thread por vez e é assim que o modelo de execução de thread único de granularidades é implementado.