スレッドのスケジューリング
すべてのスレッドには、スレッド優先順位が割り当てられます。 共通言語ランタイム内で作成されたスレッドには、初期状態で ThreadPriority.Normal の優先順位が割り当てられます。 ランタイムの外部で作成されたスレッドは、マネージド環境に入る前に設定されていた優先順位を維持します。 任意のスレッドの優先順位を取得または設定するには、Thread.Priority プロパティを使用します。
スレッドの実行は、優先度に基づいてスケジュールされます。 スレッドがランタイム内で実行されている場合でも、オペレーティング システムは、すべてのスレッドにプロセッサ タイム スライスを割り当てます。 スレッドの実行順序を決定するために使用されるスケジューリング アルゴリズムの詳細は、オペレーティング システムによって異なります。 オペレーティング システムによっては、実行可能なスレッドの中で最も優先順位の高いスレッドが常に最初に実行されるようにスケジュールされます。 同じ優先順位を持つ複数のスレッドがすべて実行可能である場合、スケジューラは、その優先順位にあるスレッド間を循環することで各スレッドに一定の実行用タイム スライスを与えます。 より優先順位の高いスレッドが実行可能である限り、それよりも優先順位の低いスレッドは実行されません。 特定の優先順位を持つ実行可能なスレッドがなくなると、スケジューラは次に低い優先順位に移り、その優先順位にあるスレッドの実行をスケジュールします。 より優先順位の高いスレッドが実行可能になった場合も、それよりも優先順位の低いスレッドの代わりに優先順位の高いスレッドが実行されます。 また、オペレーティング システムは、アプリケーションのユーザー インターフェイスをフォアグラウンドとバックグラウンド間で移動させながら、スレッドの優先順位を動的に調整することもできます。 オペレーティング システムによっては、別のスケジューリング アルゴリズムが使用される場合があります。
例
以下は Thread.Priority 列挙の 5 つの優先順位すべてに対する 9 個のスレッドの実行例であり、最後の 5 個の優先度が最も高くなっています。 また、前の記事のコールバック サポートがあり、このコンテキストでは、スレッドの初期化と優先順位付けの順序が、後続のコードやプロセス実行の開始順序に必ずしも反映されるとは限らないことを示しています。 つまり、ここには、コード実行の並列性と、オペレーティング システムによって割り当てられた各スレッドのプロセッサ タイム スライスのデモンストレーションが示されています。 これにより、スレッドが実行されている環境の影響と制御が強調されます。 そうは言うものの、実際に実行の優先度を受け取っているのが、最も優先順位の高いスレッドであることは確かにわかります。
次のコードでは、実行ごとに任意の結果が生成されます。 しかし、コードを複数回実行し、出力を分析すると、実行される優先順位の一般的なシーケンス パターンを確認できるでしょう。
namespace snippets;
public class SchedulingThreads
{
public void RunMultipleThreadsOnDifferentPriorities()
{
var threadsList = new List<Thread>(9);
// Initialize 9 threads. 5 with Highest priority, and the first 4 from Lowest to Normal range.
for (int i = 0; i < 9; i++)
{
var thread = new Thread(() => { new ThreadWithCallback(Callback).Process(); });
if (i > 3)
thread.Priority = ThreadPriority.Highest;
else
thread.Priority = (ThreadPriority)i;
threadsList.Add(thread);
}
threadsList.ForEach(thread => thread.Start());
}
public void Callback(ThreadPriority threadPriority)
{
Console.WriteLine($"Callback in {threadPriority} priority. \t\t ThreadId: {Thread.CurrentThread.ManagedThreadId}.");
}
public class ThreadWithCallback
{
public ThreadWithCallback(Action<ThreadPriority> callback)
{
this.callback = callback;
}
public Action<ThreadPriority> callback;
public void Process()
{
Console.WriteLine($"Entered process in {Thread.CurrentThread.Priority} priority. \t\t ThreadId: {Thread.CurrentThread.ManagedThreadId}.");
Thread.Sleep(1000);
Console.WriteLine($"Finished process in {Thread.CurrentThread.Priority} priority. \t\t ThreadId: {Thread.CurrentThread.ManagedThreadId}.");
if (callback != null)
{
callback(Thread.CurrentThread.Priority);
}
}
}
// The example displays the output like the following:
// Entered process in Highest priority. ThreadId: 9.
// Entered process in Highest priority. ThreadId: 12.
// Entered process in Normal priority. ThreadId: 6.
// Entered process in BelowNormal priority. ThreadId: 5.
// Entered process in Lowest priority. ThreadId: 4.
// Entered process in AboveNormal priority. ThreadId: 7.
// Entered process in Highest priority. ThreadId: 11.
// Entered process in Highest priority. ThreadId: 10.
// Entered process in Highest priority. ThreadId: 8.
// Finished process in Highest priority. ThreadId: 9.
// Finished process in Highest priority. ThreadId: 12.
// Finished process in Highest priority. ThreadId: 8.
// Finished process in Highest priority. ThreadId: 10.
// Callback in Highest priority. ThreadId: 10.
// Finished process in AboveNormal priority. ThreadId: 7.
// Callback in AboveNormal priority. ThreadId: 7.
// Finished process in Lowest priority. ThreadId: 4.
// Callback in Lowest priority. ThreadId: 4.
// Finished process in Normal priority. ThreadId: 6.
// Callback in Highest priority. ThreadId: 9.
// Callback in Highest priority. ThreadId: 8.
// Callback in Highest priority. ThreadId: 12.
// Finished process in Highest priority. ThreadId: 11.
// Callback in Highest priority. ThreadId: 11.
// Callback in Normal priority. ThreadId: 6.
// Finished process in BelowNormal priority. ThreadId: 5.
// Callback in BelowNormal priority. ThreadId: 5.
}
関連項目
.NET