2008-09-19 13 views
13

想象一下,我有一個函數可以檢查一百萬個/十億個字符串並檢查它們中的smth。在C#中使用多線程加速循環(問題)

f.ex:

foreach (String item in ListOfStrings) 
{ 
    result.add(CalculateSmth(item)); 
} 

它消耗的時間很多的,因爲CalculateSmth是非常耗時的功能。

我想問:如何在這個過程中集成多線程?

f.ex:我想火起來5個線程和他們每個人都返回了一定的效果,並且那去,直到上的列表中有項目。

也許任何人都可以顯示一些例子或文章..

忘記提到我需要它在.NET 2.0

+0

你需要的結果早在相同的順序? – Keith 2008-09-19 12:22:38

+0

你可以使用多個後臺工作者嗎? 建立某種邏輯,將採取字符串列表的數量,然後創建的BW的X量和divy了在這種情況下,每一個 – Crash893 2009-05-05 17:53:33

回答

17

你可以嘗試Parallel extensions(的.NET 4.0部分)

這些允許你寫的東西,如:

Parallel.Foreach (ListOfStrings, (item) => 
    result.add(CalculateSmth(item)); 
); 

當然result.add會需要線程安全的。

+0

,會不會有結果集合中的任何競爭條件?畢竟多線程,可以同時執行result.add ... – cruizer 2008-09-19 07:44:53

+0

result.add必須是線程安全的啊.. – Tobi 2008-09-19 07:45:43

1

不,我這裏有什麼好文章的權利,但你想要做的是沿着生產者 - 消費者的東西用線程池。

製片遍歷並創建任務(在這種情況下可能只是排隊的列表或堆棧中的項目)。消費者例如是五個線程,從堆棧讀取一個項目,通過計算消耗它,然後將其存儲在其他位置。

通過這種方式,多線程僅限於只是那些五個線程,他們都將有工作要做,直到堆棧爲空。

事情要考慮:輸入和輸出列表上

  • 戴上保護,如互斥。
  • 如果訂單很重要,請確保輸出訂單得到維護。一個例子可能是將它們存儲在SortedList或類似的東西中。
  • 確保CalculateSmth是線程安全的,它不使用任何全局狀態。
2

你必須回答的第一個問題是你是否應該使用線程

如果你的函數CalculateSmth()基本上是CPU密集型的,即在CPU使用率重基本沒有I/O使用率,那麼我很難看到使用線程的重點,因爲線程將在相同的資源上競爭,在這種情況下是CPU。

如果您CalculateSmth()同時使用CPU和I/O,那麼它可能是在使用線程的一個點。

我完全同意評論我的答案。我做了一個錯誤的假設,我們談論的是一個單核CPU,但現在我們擁有多核CPU,這是我的錯。

18

並行擴展是很酷,但是這也只是通過使用線程池這樣進行:

using System.Collections.Generic; 
using System.Threading; 

namespace noocyte.Threading 
{ 
    class CalcState 
    { 
     public CalcState(ManualResetEvent reset, string input) { 
      Reset = reset; 
      Input = input; 
     } 
     public ManualResetEvent Reset { get; private set; } 
     public string Input { get; set; } 
    } 

    class CalculateMT 
    { 
     List<string> result = new List<string>(); 
     List<ManualResetEvent> events = new List<ManualResetEvent>(); 

     private void Calc() { 
      List<string> aList = new List<string>(); 
      aList.Add("test"); 

      foreach (var item in aList) 
      { 
       CalcState cs = new CalcState(new ManualResetEvent(false), item); 
       events.Add(cs.Reset); 
       ThreadPool.QueueUserWorkItem(new WaitCallback(Calculate), cs); 
      } 
      WaitHandle.WaitAll(events.ToArray()); 
     } 

     private void Calculate(object s) 
     { 
      CalcState cs = s as CalcState; 
      cs.Reset.Set(); 
      result.Add(cs.Input); 
     } 
    } 
} 
12

注意併發不會奇蹟般地給你更多的資源。你需要確定什麼是減慢CalculateSmth。

例如,如果它是CPU綁定(和你使用的是單核),那麼同樣數量的CPU的蜱會去的代碼,你是否執行它們順序或並行。另外你會從管理線程中獲得一些開銷。同樣的道理也適用於其他約束(例如I/O)

你只能得到這個性能提升,如果CalculateSmth在其執行過程中留下的免費資源,這可能是由另一個實例使用。這並不罕見。例如,如果任務涉及IO後面跟着一些CPU資源,那麼進程1可能在執行IO時執行CPU資料。正如墊子指出的那樣,如果有基礎設施,生產者 - 消費者單位鏈可以實現這一點。

5

你需要分離你想要做的並行工作。這裏是你如何一分爲二的工作的例子:

List<string> work = (some list with lots of strings) 

// Split the work in two 
List<string> odd = new List<string>(); 
List<string> even = new List<string>(); 
for (int i = 0; i < work.Count; i++) 
{ 
    if (i % 2 == 0) 
    { 
     even.Add(work[i]); 
    } 
    else 
    { 
     odd.Add(work[i]); 
    } 
} 

// Set up to worker delegates 
List<Foo> oddResult = new List<Foo>(); 
Action oddWork = delegate { foreach (string item in odd) oddResult.Add(CalculateSmth(item)); }; 

List<Foo> evenResult = new List<Foo>(); 
Action evenWork = delegate { foreach (string item in even) evenResult.Add(CalculateSmth(item)); }; 

// Run two delegates asynchronously 
IAsyncResult evenHandle = evenWork.BeginInvoke(null, null); 
IAsyncResult oddHandle = oddWork.BeginInvoke(null, null); 

// Wait for both to finish 
evenWork.EndInvoke(evenHandle); 
oddWork.EndInvoke(oddHandle); 

// Merge the results from the two jobs 
List<Foo> allResults = new List<Foo>(); 
allResults.AddRange(oddResult); 
allResults.AddRange(evenResult); 

return allResults;