Partilhar via


Visão geral do agendamento

Existem duas formas de agendamento em Orleans que são relevantes para os grãos:

  1. Agendamento de solicitações, o agendamento de chamadas de grãos de entrada para execução de acordo com as regras de agendamento discutidas em Agendamento de solicitação.
  2. Agendamento de tarefas, o agendamento de blocos síncronos de código a serem executados de forma de thread único

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

Agendamento de tarefas

Para entender melhor o agendamento, considere o seguinte grão MyGrain, que tem um método chamado DelayExecution() que registra uma mensagem, espera algum tempo e, em seguida, 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 é executado, o corpo do método será executado em duas partes:

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

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

Aqui está 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 e Orleans é como o agendamento de tarefas no .NET funciona: métodos assíncronos em C# são convertidos em uma máquina de estado assíncrona pelo compilador e a execução progride através da máquina de estado assíncrona em etapas discretas. Cada etapa é agendada no atual TaskScheduler (acessado via TaskScheduler.Current, padrão para TaskScheduler.Default) ou no atual SynchronizationContext. Se um TaskScheduler está sendo usado, cada etapa do método é representada por uma Task instância que é passada para esse TaskScheduler. Portanto, um Task em .NET pode representar duas coisas conceituais:

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

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

Agendamento de tarefas em Orleans

Cada ativação de grão tem sua própria TaskScheduler instância que é responsável por aplicar o modelo de execução de thread único de grãos. Internamente, isso TaskScheduler é implementado via ActivationTaskScheduler e WorkItemGroup. WorkItemGroupmantém tarefas enfileiradas em um Queue<T> onde é um Task internamente e implementa IThreadPoolWorkItemT . Para executar cada um atualmente enfileiradoTask, agenda-se no .NET ThreadPoolWorkItemGroup . Quando o .NET ThreadPool invoca o WorkItemGroupmétodo 's, o WorkItemGroup executa as instâncias enfileiradas Task IThreadPoolWorkItem.Execute() uma a uma.

Cada grão 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 a ele. Isso inclui agendadores de grãos , bem como outros itens de trabalho, como itens de trabalho agendados via Task.Run(...):

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

Nota

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