2016-01-20 63 views
7

我有以下代碼:什麼決定了TaskFactory派生作業的線程數量?

var factory = new TaskFactory(); 
for (int i = 0; i < 100; i++) 
{ 
    var i1 = i; 
    factory.StartNew(() => foo(i1)); 
} 

static void foo(int i) 
{ 
    Thread.Sleep(1000); 
    Console.WriteLine($"foo{i} - on thread {Thread.CurrentThread.ManagedThreadId}"); 
}    

我可以看到它只做4個線程在同一時間(根據觀察)。我的問題:

  1. 什麼決定一次使用的線程數?
  2. 我該如何檢索這個號碼?
  3. 如何更改此號碼?

P.S.我的盒子有4個核心。

P.P.S.我需要有任務的具體數量(沒有更多)是同時由TPL處理,並結束了與下面的代碼:

private static int count = 0; // keep track of how many concurrent tasks are running 

private static void SemaphoreImplementation() 
{ 
    var s = new Semaphore(20, 20); // allow 20 tasks at a time 

    for (int i = 0; i < 1000; i++) 
    { 
     var i1 = i; 

     Task.Factory.StartNew(() => 
     { 
      try 
      {       
       s.WaitOne(); 
       Interlocked.Increment(ref count); 

       foo(i1); 
      } 
      finally 
      { 
       s.Release(); 
       Interlocked.Decrement(ref count); 
      } 
     }, TaskCreationOptions.LongRunning); 
    } 
} 

static void foo(int i) 
{ 
    Thread.Sleep(100); 
    Console.WriteLine($"foo{i:00} - on thread " + 
      $"{Thread.CurrentThread.ManagedThreadId:00}. Executing concurently: {count}"); 
} 
+2

CPU核心數量。 – Enigmativity

+2

@Enigmativity內核數量對於IO綁定工作負載沒有意義,並且TPL對IO無能爲力。 – usr

+0

@Enigmativity因此,TPL的默認規則是讓最大線程數等於CPU數? – AngryHacker

回答

10

當您使用.NET中Task,你說的是TPL安排在ThreadPool上執行一項工作(通過TaskScheduler)執行。請注意,工作將盡早安排,但計劃人員認爲合適。這意味着TaskScheduler將決定將使用多少個線程來運行任務數量以及在哪個線程上執行哪個任務。

TPL調整得非常好,並在執行任務時繼續調整算法。所以,在大多數情況下,它會盡量減少爭用。這意味着如果你正在運行100個任務並且只有4個內核(你可以使用Environment.ProcessorCount),那麼在任何給定的時間執行超過4個線程都沒有意義,否則就需要做更多的上下文切換。現在有些時候你想明確地覆蓋這種行爲。假設你需要等待某種IO完成,這是一個完全不同的故事

總之,請相信TPL。但如果你是堅定的產卵每個任務一個線程(!並不總是一個好主意),你可以使用:

Task.Factory.StartNew(
    () => /* your piece of work */, 
    TaskCreationOptions.LongRunning); 

這告訴默認Taskscheduler明確地生成一個新的線程那件工作。

您也可以使用您自己的Scheduler並將它傳遞給TaskFactory。你可以找到一大堆SchedulersHERE

注意另一種選擇是使用PLINQ默認情況下,再次分析您的查詢,並在阻塞的IO,你一定啓動多個線程將結果的情況下決定是否並行化會產生任何益處或沒有,再在一個更好的執行可以強制並行使用WithExecutionMode(ParallelExecutionMode.ForceParallelism)那麼你可以使用WithDegreeOfParallelism,給多少線程使用提示,但記得有不能保證你會得到很多線程,MSDN說:

設置在查詢中使用的並行度。 並行度是最大數同時執行任務 將用於處理查詢。

最後,我高度建議有上ThreadingTPLTHIS大系列文章一讀。

+2

TPL不知道工作負載是做什麼的。它*不可能針對IO進行很好的調整。 TPL「會做正確的事情」的想法在整個網絡中重複出現,但是是錯誤的。這個問題就是一個很好的例子:4線程是吞吐量限制的選擇。 – usr

+1

因此我爲什麼說:「一個完全不同的故事」! – 2016-01-21 12:15:29

+1

@usr完全是我的想法。 TPL沒有辦法知道我的工作流程。我最終用'TaskCreationOptions.LongRunning'加上一個Semaphore來實現它。 – AngryHacker

4

如果您將任務數量增加到例如1000000,您將看到隨着時間的推移會產生更多的線程。 TPL傾向於每500毫秒注入一次。

TPL線程池不理解IO綁定的工作負載(睡眠是IO)。在這些情況下依靠TPL來選擇正確的並行度並不是一個好主意。 TPL完全無能爲力,並基於對吞吐量的模糊猜測而注入更多線程。也爲了避免死鎖。

在這裏,TPL政策顯然沒有用,因爲您添加的線程越多,獲得的吞吐量就越多。在這種設計的情況下,每個線程可以每秒處理一個項目。 TPL對此不知情。將線程數限制爲內核數是沒有意義的。

什麼決定一次使用的線程數量?

很少記錄TPL啓發式。他們經常出問題。特別是在這種情況下,它們將隨着時間產生無限數量的線程。使用任務管理器自己查看。讓它運行一個小時,你將有1000線程。

我該如何找回這個號碼?我怎樣才能改變這個號碼?

可以檢索這些數字一些但是,這並不是正確的方式去。如果您需要有保證的DOP,您可以使用AsParallel().WithDegreeOfParallelism(...)或自定義任務計劃程序。您也可以手動啓動LongRunning任務。不要混淆進程全局設置。

+1

DOP是什麼意思?在阻塞IO的情況下,使用'AsParallel()',你仍然需要通過使用WithDegreeOfParallelism來提示它,否則它認爲工作是CPU限制的,並且在4覈計算機上分配不同的線程'PLINQ'可能會運行同時只有4個任務。 – MaYaN

+0

@MaYaN是的,無論你怎麼做,你總是需要指定一個包含IO的DegreeOfParallelism(= DOP)。這是一個關鍵點:沒有圖書館可以知道正確的價值。你需要憑經驗找到它(或通過人類猜測)。 – usr

相關問題