Поделиться через


Обзор планирования

Существует две формы планирования, в Orleans которых имеют отношение к зернам:

  1. Планирование запросов, планирование входящих вызовов зерна для выполнения в соответствии с правилами планирования, рассмотренными в планировании запросов.
  2. Планирование задач, планирование синхронных блоков кода, выполняемых одним потоком

Весь код зерна выполняется на планировщике задач зерна, что означает, что запросы также выполняются на планировщике задач зерна. Даже если правила планирования запросов позволяют одновременно выполнять несколько запросов, они не будут выполняться параллельно, так как планировщик задач зерна всегда выполняет задачи по одному и, следовательно, никогда не выполняет несколько задач параллельно.

Планирование задач

Чтобы лучше понять планирование, рассмотрим следующее зерно, который имеет метод, MyGrainкоторый называется DelayExecution() журналом сообщения, ожидает некоторое время, а затем записывает другое сообщение перед возвратом.

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

При выполнении этого метода текст метода будет выполняться в двух частях:

  1. Первый _logger.LogInformation(...) вызов и вызов Task.Delay(1_000).
  2. Второй _logger.LogInformation(...) вызов.

Вторая задача не будет запланирована в планировщике задач зерна до Task.Delay(1_000) завершения вызова, на котором он запланирует продолжение метода зерна.

Ниже приведено графическое представление о планировании и выполнении запроса в виде двух задач:

Two-Task-based request execution example.

Приведенное выше описание не относится к Orleans планированию задач в .NET: асинхронные методы в C# преобразуются в асинхронный компьютер состояния компилятором и выполнение выполняется с помощью асинхронного компьютера состояния в дискретных шагах. Каждый шаг запланирован на текущий TaskScheduler (доступ TaskScheduler.Currentпо умолчанию TaskScheduler.Defaultили текущий SynchronizationContext). TaskScheduler Если используется, каждый шаг в методе представлен экземпляромTask, который передается в этоTaskScheduler. Таким образом, в Task .NET можно представить две концептуальные вещи:

  1. Асинхронная операция, которую можно ожидать. Выполнение приведенного DelayExecution() выше метода представлено ожидаемым Task .
  2. В синхронном блоке работы каждый этап в приведенном DelayExecution() выше методе представлен в виде .Task

При TaskScheduler.Default использовании продолжение планируется непосредственно в .NET ThreadPool и не упаковывается в Task объект. Оболочка продолжения в Task экземплярах происходит прозрачно, поэтому разработчикам редко нужно учитывать эти сведения о реализации.

Планирование задач в Orleans

Каждая активация зерна имеет собственный TaskScheduler экземпляр, который отвечает за применение модели однопоточного выполнения зерна. Внутри системы это TaskScheduler реализуется с помощью ActivationTaskScheduler и WorkItemGroup. WorkItemGroup сохраняет вложенные задачи в Queue<T> месте, где T находится Task внутренне и реализует IThreadPoolWorkItem. Для выполнения каждого в настоящее время, запланированного WorkItemGroup Taskв .NETThreadPool. При вызове WorkItemGroupIThreadPoolWorkItem.Execute() метода WorkItemGroup .NET ThreadPool выполняет закресченные Task экземпляры по одному.

У каждого зерна есть планировщик, который выполняется путем планирования в .NET ThreadPool:

Orleans grains scheduling themselves on the .NET ThreadPool.

Каждый планировщик содержит очередь задач:

Scheduler queue of scheduled tasks.

.NET ThreadPool выполняет каждый рабочий элемент, заквеченный в него. Это включает планировщики зерна, а также другие рабочие элементы, такие как рабочие элементы, запланированные с помощью Task.Run(...):

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

Примечание.

Планировщик зерна может выполняться только в одном потоке за раз, но он не всегда выполняется в одном потоке. .NET ThreadPool может использовать разные потоки при каждом выполнении планировщика зерна. Планировщик зерна отвечает за то, что он выполняется только в одном потоке за раз, и это то, как реализуется однопоточная модель выполнения зерна.