Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Две формы планирования Orleans являются актуальными для зерновых культур:
- Планирование запросов: планирование входящих вызовов зерна для выполнения в соответствии с правилами, рассмотренными в планировании запросов.
- Планирование задач: выполнение синхронных блоков кода в однопоточном режиме.
Весь код зерна выполняется на планировщике задач зерна, что означает, что запросы также выполняются на планировщике задач зерна. Даже если правила планирования запросов позволяют одновременно выполнять несколько запросов, они не будут выполняться параллельно , так как планировщик задач зерна всегда выполняет задачи по одному и никогда не выполняет несколько задач параллельно.
Планирование задач
Чтобы лучше понять планирование, рассмотрим следующее зерно 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");
}
}
При выполнении этого метода текст метода выполняется в двух частях:
- Первый
_logger.LogInformation(...)вызов и вызовTask.Delay(1_000). - Второй
_logger.LogInformation(...)вызов.
Вторая задача не будет назначена диспетчеру задач для зерна до завершения вызова Task.Delay(1_000). На этом этапе он планирует продолжение метода зерна.
Ниже приведено графическое представление о планировании и выполнении запроса в виде двух задач:
Приведенное выше описание не является специфичным для Orleans; в нем объясняется, как работает планирование задач в технологии .NET. Компилятор C# преобразует асинхронные методы в асинхронный конечный автомат, и выполнение прогрессирует через этот конечный автомат дискретными шагами. Каждый шаг планируется для текущего TaskScheduler (доступ к нему осуществляется через TaskScheduler.Current, с использованием TaskScheduler.Default по умолчанию) или текущего SynchronizationContext. Если используется TaskScheduler, каждый шаг в методе представляет собой экземпляр Task, переданный в этот TaskScheduler. Таким образом, в Task .NET можно представить две концептуальные вещи:
- Асинхронная операция, которую можно ожидать. Выполнение приведенного
DelayExecution()выше метода представлено ожидаемым Task . - Синхронный блок работы. Каждый этап в приведенном выше методе
DelayExecution()представлен в виде Task.
При использовании TaskScheduler.Default продолжение располагается непосредственно в среде .NET ThreadPool и не оборачивается в объект Task. Обёртывание продолжений в экземплярах Task выполняется прозрачно, поэтому разработчикам редко приходится задумываться об этих деталях реализации.
Планирование задач в Orleans
Каждая активация зерна имеет собственный TaskScheduler экземпляр, обеспечивающий выполнение модели однопоточного исполнения зерен. Внутри системы это TaskScheduler реализуется с помощью ActivationTaskScheduler и WorkItemGroup.
WorkItemGroup сохраняет вложенные задачи в Queue<T> (где T находится внутри) Taskи реализует IThreadPoolWorkItem. Для выполнения каждой из текущих задач Task, WorkItemGroup планирует сам(а) себя в .NET ThreadPool. При вызове метода ThreadPoolWorkItemGroupIThreadPoolWorkItem.Execute() .NET WorkItemGroup выполняет поставленные в очередь экземпляры Task по одному.
У каждого зерна есть планировщик, который выполняется путем планирования в .NET ThreadPool:
Каждый планировщик содержит очередь задач:
.NET ThreadPool выполняет каждый рабочий элемент, заквеченный в него. Это включает программы планирования зерновых, а также другие рабочие элементы, такие как запланированные с помощью Task.Run(...):
Примечание.
Планировщик зерна может выполняться только в одном потоке одновременно, но он не всегда выполняется в одном потоке. .NET ThreadPool может использовать разные потоки каждый раз, когда выполняется планировщик зерен. Планировщик зерен гарантирует, что он выполняется только в одном потоке за раз, реализуя модель однопоточного исполнения зерен.