排程執行緒
每個執行緒都擁有一個指派的執行緒優先權。 Common Language Runtime 中建立的執行緒,一開始會被指派 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.
}