共用方式為


排程概觀

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,則方法中的每個步驟都會以傳遞給該 TaskSchedulerTask 執行個體來表示。 因此,.NET 中的 Task 可以代表兩個概念性事項:

  1. 可以等候的非同步作業。 上述 DelayExecution() 方法的執行是由可以等候的 Task 來表示。
  2. 在同步的工作區塊中,上述 DelayExecution() 方法中的每個階段都會以 Task 表示。

TaskScheduler.Default 為使用中時,接續會直接排程至 .NET ThreadPool,而不會包裝在 Task 物件中。 Task 執行個體中的接續包裝會以透明方式發生,因此開發人員很少需要注意這些實作詳細資料。

Orleans 中的工作排程

每個粒紋啟用都有自己的 TaskScheduler 執行個體,負責強制執行粒紋的「單一執行緒」執行模型。 在內部,這個 TaskScheduler 會透過 ActivationTaskSchedulerWorkItemGroup 實作。 WorkItemGroup 會在 Queue<T> 中保留排入佇列的工作,其中 T 是內部的 Task 且會實作 IThreadPoolWorkItem。 若要執行每個目前排入佇列的 TaskWorkItemGroup 會在 .NET ThreadPool 上排程其「本身」。 當 .NET ThreadPool 叫用 WorkItemGroupIThreadPoolWorkItem.Execute() 方法時,WorkItemGroup 會逐一執行排入佇列的 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 都可以使用不同的執行緒。 粒紋的排程器負責確保其一次只會在一個執行緒上執行,這是實作粒紋的單一執行緒執行模型的方法。