Dot-Net

為什麼 TaskFactory.StartNew Task 沒有立即啟動?

  • October 24, 2017

根據MSDN 文件 TaskFactory.StartNew它創建並啟動一個任務。所以,對於下面的程式碼範例

class Program
{
   public static void Main()
   {
       var t =Task.Factory.StartNew(
               () => SomeLongRunningCalculation(10, Display)
           );
       var t1 = Task.Factory.StartNew(
               () => SomeLongRunningCalculation(20, Display)
           );
       Console.WriteLine("Invoked tasks");
       Task.WaitAll(t, t1);
       Console.ReadLine();
   }

   public static void Display(int value)
   {
       Console.WriteLine(value);
   }

   public static void SomeLongRunningCalculation(int j, Action<int> callBack)
   {
       Console.WriteLine("Invoking calculation for {0}", j);
       System.Threading.Thread.Sleep(1000);
       if (callBack != null)
       {
           callBack(j + 1);
       }
   }
}      

我的預期輸出是

Invoking calculation for 10
Invoking calculation for 20
Invoked tasks
11 
21

但是,它正在顯示

Invoked tasks
Invoking calculation for 20
Invoking calculation for 10
21
11

我樂意去學

  1. 為什麼在 StartNew 之後任務沒有立即執行?
  2. 我應該怎麼做才能獲得預期格式的輸出?

這在具有單核 CPU 的機器上很可能出現。或者可能在一個多核 cpu 上,它也忙於做其他事情。

創建一個任務或一個執行緒只建立一個允許程式碼執行的邏輯作業系統結構。如果核心很忙,作業系統調度程序不會立即開始執行它,執行緒必須與機器上執行的所有其他執行緒競爭。一個典型的 Windows 會話有上千個左右。每秒 64 次,核心生成一個中斷,調度程序重新評估正在發生的事情,看看是否應該輪到另一個執行緒。任何未阻塞的執行緒(等待其他執行緒完成工作,例如讀取文件或網路數據包)都有資格執行,調度程序會選擇具有最高優先級的執行緒。調度程序中的一些附加程式碼會修改優先級值,以確保所有執行緒都有機會。

機會是這裡的關鍵詞。執行緒調度是不確定的

請注意我從未說過任何關於 Thread 或 Task 或 ThreadPool 類的內容。他們沒有能力對作業系統調度執行緒的方式做任何事情。唯一可能的是阻止執行緒執行,這是執行緒池調度程序的工作。

優先級很重要,您可以修改 Thread.Priority 或 Task.Priority 屬性來影響結果。但是沒有 slamdunk,作業系統調度程序會不斷地從您使用該屬性設置的基本優先級調整執行緒優先級。例如,您不能通過讓另一個具有更高優先級的執行緒來阻止執行緒執行*。*

希望執行緒以可預測的順序執行會導致最糟糕的錯誤,即執行緒競爭錯誤。第二個最壞的情況是僵局。它們極難調試,因為它們依賴於時間和可用的機器資源和負載。您只能通過編寫明確處理它的程式碼來確保獲得特定訂單。您可以使用 Mutex 或lock關鍵字等執行緒原語來執行此操作。值得注意的是,當您嘗試將此類程式碼添加到您的程式碼段時,您最終會得到一個不再具有並發性的程序。或者換句話說,您最終將不再使用任務。一個執行緒或任務只有在你負擔得起它在不可預測的時間執行時才會有用。

引用自:https://stackoverflow.com/questions/10266221