Обзор планирования
Существует две формы планирования, в 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. Для выполнения каждого в настоящее время, запланированного WorkItemGroup
Task
в .NETThreadPool
. При вызове WorkItemGroup
IThreadPoolWorkItem.Execute()
метода WorkItemGroup
.NET ThreadPool
выполняет закресченные Task
экземпляры по одному.
У каждого зерна есть планировщик, который выполняется путем планирования в .NET ThreadPool
:
Каждый планировщик содержит очередь задач:
.NET ThreadPool
выполняет каждый рабочий элемент, заквеченный в него. Это включает планировщики зерна, а также другие рабочие элементы, такие как рабочие элементы, запланированные с помощью Task.Run(...)
:
Примечание.
Планировщик зерна может выполняться только в одном потоке за раз, но он не всегда выполняется в одном потоке. .NET ThreadPool
может использовать разные потоки при каждом выполнении планировщика зерна. Планировщик зерна отвечает за то, что он выполняется только в одном потоке за раз, и это то, как реализуется однопоточная модель выполнения зерна.