次の方法で共有


スケジュール設定の概要

Orleans のスケジュール設定には、グレインに関連する形式が 2 つあります。

  1. 要求のスケジュール設定。要求のスケジュール設定で説明されているスケジュール設定ルールに従って実行するための受信グレイン呼び出しのスケジュール設定。
  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. 1 番目の _logger.LogInformation(...) 呼び出しと Task.Delay(1_000) 呼び出し。
  2. 2 番目の _logger.LogInformation(...) 呼び出し。

2 番目のタスクは、Task.Delay(1_000) 呼び出しが完了するまで粒度のタスク スケジューラにスケジュールされず、完了時点で粒度メソッドの "継続" がスケジュールされます。

次の図に、どのように要求がスケジュールされ、2 つのタスクとして実行されるかを示します。

Two-Task-based request execution example.

上記は Orleans に固有ではなく、.NET でのタスクのスケジュール設定のしくみを説明しています。C# の非同期メソッドはコンパイラによって非同期状態機械に変換され、個別のステップの非同期状態機械を経て実行が進行します。 各ステップは、現在の TaskScheduler (TaskScheduler.Current を介してアクセス、既定は TaskScheduler.Default) または現在の SynchronizationContext でスケジュールされます。 TaskScheduler が使用されている場合、メソッドの各ステップは、その TaskScheduler に渡される Task インスタンスで表されます。 したがって、.NET の Task は、次の 2 つの概念を表すことができます。

  1. 待機可能な非同期操作。 上記の DelayExecution() メソッドの実行は、待機可能な Task メソッドで表すことができます。
  2. 同期作業ブロックでは、上記の DelayExecution() メソッド内の各ステージは Task で表されます。

TaskScheduler.Default が使用中の場合、継続は .NET ThreadPool に直接スケジュールされ、Task オブジェクトでラップされません。 Task インスタンスでの継続のラップは透過的に行われるため、開発者がこれらの実装の詳細を認識する必要はほとんどありません。

Orleans のタスク スケジュール

各粒度のアクティブ化には、粒度の "シングルスレッド" 実行モデルの適用を担う独自の TaskScheduler インスタンスがあります。 内部的には、この TaskSchedulerActivationTaskSchedulerWorkItemGroup を介して実装されます。 WorkItemGroup は、エンキューされたタスクを Queue<T> に保持します。ここで、T は内部的には Task であり、IThreadPoolWorkItem を実装します。 現在エンキューされている各 Task を実行するには、WorkItemGroup によって .NET ThreadPool で "それ自体" がスケジュールされます。 .NET ThreadPool によって WorkItemGroupIThreadPoolWorkItem.Execute() メソッドが呼び出されると、WorkItemGroup によって、エンキューされた Task インスタンスが 1 つずつ実行されます。

粒度ごとに、.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.

Note

粒度のスケジューラは一度に 1 つのスレッドでのみ実行できますが、常に同じスレッドで実行されるとは限りません。 .NET ThreadPool では、粒度のスケジューラが実行されるたびに別のスレッドを自由に使用できます。 粒度のスケジューラは、一度に 1 つのスレッドでのみ実行されるようにする役割を担い、このように、粒度の "シングルスレッド" 実行モデルが実装されます。