スケジュール設定の概要
Orleans のスケジュール設定には、グレインに関連する形式が 2 つあります。
- 要求のスケジュール設定。要求のスケジュール設定で説明されているスケジュール設定ルールに従って実行するための受信グレイン呼び出しのスケジュール設定。
- タスクのスケジュール設定。"シングルスレッド" 方式で実行されるコードの同期ブロックのスケジュール設定
粒度コードはすべて、粒度のタスク スケジューラで実行されます。つまり、要求も粒度のタスク スケジューラで実行されます。 要求のスケジュール設定ルールで複数の要求の "同時" 実行を許可している場合でも、"並列" では実行されません。これは、タスクが粒度のタスク スケジューラによって常に 1 つずつ実行されることで、複数のタスクが並列で実行されることがないからです。
タスクのスケジュール設定
スケジュール設定をよりよく理解するには、次の 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");
}
}
このメソッドが実行されると、メソッド本体が次の 2 つの部分で実行されます。
- 1 番目の
_logger.LogInformation(...)
呼び出しとTask.Delay(1_000)
呼び出し。 - 2 番目の
_logger.LogInformation(...)
呼び出し。
2 番目のタスクは、Task.Delay(1_000)
呼び出しが完了するまで粒度のタスク スケジューラにスケジュールされず、完了時点で粒度メソッドの "継続" がスケジュールされます。
次の図に、どのように要求がスケジュールされ、2 つのタスクとして実行されるかを示します。
上記は Orleans に固有ではなく、.NET でのタスクのスケジュール設定のしくみを説明しています。C# の非同期メソッドはコンパイラによって非同期状態機械に変換され、個別のステップの非同期状態機械を経て実行が進行します。 各ステップは、現在の TaskScheduler (TaskScheduler.Current を介してアクセス、既定は TaskScheduler.Default) または現在の SynchronizationContext でスケジュールされます。 TaskScheduler
が使用されている場合、メソッドの各ステップは、その TaskScheduler
に渡される Task
インスタンスで表されます。 したがって、.NET の Task
は、次の 2 つの概念を表すことができます。
- 待機可能な非同期操作。 上記の
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
で "それ自体" がスケジュールされます。 .NET ThreadPool
によって WorkItemGroup
の IThreadPoolWorkItem.Execute()
メソッドが呼び出されると、WorkItemGroup
によって、エンキューされた Task
インスタンスが 1 つずつ実行されます。
粒度ごとに、.NET ThreadPool
でそれ自体をスケジュールすることで実行するスケジューラがあります。
スケジューラごとに、タスクのキューが含まれます。
.NET ThreadPool
によって、それにエンキューされた各作業項目が実行されます。 これには、"粒度スケジューラ" だけでなく、Task.Run(...)
を介してスケジュールされる他の作業項目も含まれます。
Note
粒度のスケジューラは一度に 1 つのスレッドでのみ実行できますが、常に同じスレッドで実行されるとは限りません。 .NET ThreadPool
では、粒度のスケジューラが実行されるたびに別のスレッドを自由に使用できます。 粒度のスケジューラは、一度に 1 つのスレッドでのみ実行されるようにする役割を担い、このように、粒度の "シングルスレッド" 実行モデルが実装されます。
.NET