2015-10-04 97 views
4

出於測試目的,我編寫CPU壓力程序:它只是在M個線程中執行N個for-loops。 我用大量的線程運行這個程序,比如說200. 但是在任務管理器中我看到線程計數器沒有超過一些小值,比如說9和一個Thread.Start方法等待完成以前的運行線程爲什麼Thread.Start方法在CPU負載過高時被阻塞?

這種行爲看起來像一個ThreadPool行爲,但我期望定期System.Threading.Thread必須啓動無需等待某種原因。下面

代碼將重現這一問題,並有解決方法的選擇:

using System; 
using System.Diagnostics; 
using System.Threading; 

namespace HeavyLoad 
{ 
    class Program 
    { 
     static long s_loopsPerThread; 
     static ManualResetEvent s_startFlag; 

     static void Main(string[] args) 
     { 
      long totalLoops = (long)5e10; 
      int threadsCount = 200; 

      s_loopsPerThread = totalLoops/threadsCount; 

      Thread[] threads = new Thread[threadsCount]; 

      var watch = Stopwatch.StartNew(); 
      for (int i = 0; i < threadsCount; i++) 
      { 
       Thread t = new Thread(IntensiveWork); 
       t.IsBackground = true; 
       threads[i] = t; 
      } 
      watch.Stop(); 
      Console.WriteLine("Creating took {0} ms", watch.ElapsedMilliseconds); 

      // *** Comment out s_startFlag creation to change the behavior *** 
      // s_startFlag = new ManualResetEvent(false); 

      watch = Stopwatch.StartNew(); 
      foreach (var thread in threads) 
      { 
       thread.Start(); 
      } 
      watch.Stop(); 
      Console.WriteLine("Starting took {0} ms", watch.ElapsedMilliseconds); 

      if (s_startFlag != null) 
       s_startFlag.Set(); 

      watch = Stopwatch.StartNew(); 
      foreach (var thread in threads) 
      { 
       thread.Join(); 
      } 
      watch.Stop(); 
      Console.WriteLine("Waiting took {0} ms", watch.ElapsedMilliseconds); 

      Console.ReadLine(); 
     } 

     private static void IntensiveWork() 
     { 
      if (s_startFlag != null) 
       s_startFlag.WaitOne(); 

      for (long i = 0; i < s_loopsPerThread; i++) 
      { 
       // hot point 
      } 
     } 
    } 
} 

案例1:如果s_startFlag創作的評論,然後啓動線程立即開始高密集的CPU工作。在這種情況下,我有一個小的併發(約9個線程)和所有的時間我抱上線啓動代碼:

Creating took 0 ms 
Starting took 4891 ms 
Waiting took 63 ms 

案例2:但如果我創建s_startFlag,所有新的線程將等待,直到它會被設置。在這種情況下,我成功地啓動所有200個線程同時並獲得預期值:一點時間開始和很多時間線程在任務管理器中的工作,號碼是200 +:

Creating took 0 ms 
Starting took 27 ms 
Waiting took 4733 ms 

爲什麼線程垃圾開始第一種情況?我超過了什麼樣的限制?

系統:

  • 操作系統:Windows 7專業版
  • 框架:NET 4.6
  • CPU:英特爾酷四核Q9550 @ 2.83GHz
  • RAM:8千兆
+0

可能你正在尋找一個硬件/操作系統限制?例如,磁盤I/O肯定會有最大通道限制,這會導致一些線程被阻塞...... – code4life

+0

此測試不涉及任何磁盤I/O。這看起來像限制「你有CPU負載95%,所以我不啓動任何線程」。但我沒有聽說過這種限制。爲什麼會發生? –

+0

問題是,究竟是什麼導致CPU負載? CPU正在做一些事情,這就是儀器告訴你的。運行一個分析器在這一點上是非常有意義的。 – code4life

回答

1

我做了一些研究,現在我發現高CPU負載對線程啓動時間確實有很大的影響。

第一:爲了有更多的觀察時間,我設置totalLoops爲100倍大的值。我看到線程不受限制,但非常緩慢地創建。 1個線程在1-2秒內啓動!第二:我明確地將一個主線程綁定到CPU核心#0,並且使用SetThreadAffinityMask函數(https://sites.google.com/site/dotburger/threading/setthreadaffinitymask-1)將線程工作到核心#1,#2,#3。

Stopwatch watch; 
using (ProcessorAffinity.BeginAffinity(0)) 
{ 
    watch = Stopwatch.StartNew(); 
    for (int i = 0; i < threadsCount; i++) 
    { 
     Thread t = new Thread(IntensiveWork); 
     t.IsBackground = true; 
     threads[i] = t; 
    } 
    watch.Stop(); 
    Console.WriteLine("Creating took {0} ms", watch.ElapsedMilliseconds); 
} 

using (ProcessorAffinity.BeginAffinity(1, 2, 3)) 
{ 
    for (long i = 0; i < s_loopsPerThread; i++) 
    { 
    } 
} 

現在主線程擁有自己的專用CPU核心(在這個過程中的邊界)和工作線程之後開始〜10毫秒(totalLoops = 5E10)。

Creating took 0 ms 
Starting took 2282 ms 
Waiting took 3681 ms 

此外,我發現這句話在MSDN:

當你調用線程。在線程上啓動方法,該線程可能爲 或可能不會立即開始執行,具體取決於處理器的數量和當前等待執行的線程數。

https://msdn.microsoft.com/en-us/library/1c9txz50(v=vs.110).aspx

結論: Thread.Start方法非常積極的工作線程數量敏感。這可能是一個非常強大的性能影響 - 數百倍的放緩。

相關問題