2010-02-03 79 views
12

我對.NET編程和多線程一般都比較陌生,並且想知道是否可以使用.NET提供的BackgroundWorker生成工作線程以在控制檯應用程序中執行某些工作?從網上的各種文檔中,我發現這個類的意圖更多的是面向UI的應用程序,您希望在後臺做一些工作,但保持UI的響應,並報告進度,如果需要的話取消處理等。在控制檯應用程序中使用.NET BackgroundWorker類

在我的情況下,基本上我有一個控制器類,我想從多個工作線程產生一些處理(限制使用信號量產生的工作線程的最大數量)。然後我希望我的控制器類阻塞,直到所有線程都完成處理。所以在我啓動一個工作線程完成一些工作之後,我希望線程能夠在處理完成時通知控制線程。我看到我可以使用後臺worker類,並處理事件DoWork和RunWorkerCompleted來完成此任務,但是想知道這是否是個好主意?有沒有更好的方法來實現這一目標?

+2

雖然它可能不會幫你解決這個問題,.NET Framework 4中的新並行編程功能,你可能會感興趣。有些東西要留意 - 它很快就會發布。 http://msdn.microsoft.com/en-us/concurrency/default.aspx – 2010-02-03 23:15:49

回答

8

這不適用於控制檯應用程序,因爲SynchronizationContext.Current將永遠不會被初始化。當您使用GUI應用程序時,這由Windows Forms或WPF爲您初始化。

這就是說,沒有理由這樣做。只需使用ThreadPool.QueueUserWorkItem和重置事件(ManualResetEventAutoResetEvent)來捕獲完成狀態並阻止您的主線程。


編輯:

看到一些的OP的意見後,我想我會添加此。

在我看來,「最好」的替代方案將是獲得Rx Framework的副本,因爲它包含.NET 4中TPL的回送端口。這將允許您使用Parallel.ForEach的過載,它提供了選項提供ParallelOptions實例。這將允許您限制併發操作的總數,並處理所有的工作適合你:

// using collection of work items, such as: List<Action<object>> workItems; 
var options = new ParallelOptions(); 
options.MaxDegreeOfParallelism = 10; // Restrict to 10 threads, not recommended! 

// Perform all actions in the list, in parallel 
Parallel.ForEach(workItems, options, item => { item(null); }); 

然而,使用Parallel.ForEach,我會親自讓系統管理的並行度。它會自動分配適當數量的線程(尤其是當/如果這轉移到.NET 4)。

9

如果您的要求僅僅是阻塞,直到所有的線程完成,那真的很容易 - 只需啓動新線程,然後調用Thread.Join他們每個人:

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

public class Test 
{ 
    static void Main() 
    { 
     var threads = new List<Thread>(); 
     for (int i = 0; i < 10; i++) 
     { 
      int copy = i; 
      Thread thread = new Thread(() => DoWork(copy)); 
      thread.Start(); 
      threads.Add(thread); 
     } 

     Console.WriteLine("Main thread blocking"); 
     foreach (Thread thread in threads) 
     { 
      thread.Join(); 
     } 
     Console.WriteLine("Main thread finished"); 
    } 

    static void DoWork(int thread) 
    { 
     Console.WriteLine("Thread {0} doing work", thread); 
     Random rng = new Random(thread); // Seed with unique numbers 
     Thread.Sleep(rng.Next(2000)); 
     Console.WriteLine("Thread {0} done", thread); 
    } 
} 

編輯:如果你有機會獲得。 NET 4.0,那麼TPL絕對是正確的選擇。否則,我會建議使用生產者/消費者隊列(有大量的示例代碼)。基本上你有一個工作項目隊列,並且有多少個核心線程(假設它們是CPU限制的;你想要根據你的工作量來調整它)。每個消費者線程將從隊列中取出項目並逐個處理它們。具體如何管理這取決於你的情況,但並不複雜。如果您能夠提出開始所需的所有工作,則更容易,以便線程在發現隊列爲空時可以退出。

+0

這增加了相當多的啓動時間比使用線程池線程,但是。啓動你自己的線程並不是很快,而且可能是不必要的。 – 2010-02-03 23:45:12

+1

@Reed:除非線程池已經被預熱,否則這個成本將會在那裏......而且它啓動線程真的很不昂貴。在我的**上網本**上啓動並加入1000個線程需要不到1秒的時間。我不認爲這是「相當一段時間的啓動時間」 - 不管怎樣,OP不太可能真的需要1000個線程。我認爲這是比使用線程池更簡單的解決方案。 – 2010-02-04 07:08:05

+0

喬恩,但與thread.join,我只是阻止主線程,直到它完成。然而,如果我想在特定線程完成時得到通知,那麼我可以在主線程中做一些進一步的操作? – jvtech 2010-02-04 16:14:32

1

你是對的,BackgroundWorker不會在這裏工作。它確實被設計爲與GUI環境(WinForms和WPF)一起工作,其中主線程永久忙於檢查/執行消息泵(處理所有Windows事件,如Click和Resize,加上來自backgroundWorker的那些事件)。 沒有事件隊列,主線程將耗盡。如果沒有事件隊列,BackgroundWorker無法調用主線程上的回調。

事實上,在控制檯上執行多線程操作十分困難,因爲您沒有Message Pump來​​解決兩個大問題:如何讓主線程保持活動狀態以及如何通知UI上需要的主線程更改。基於事件的編程被證明是多線程理想的學習基礎。

有一些解決方案:

使用消息泵擴展您的控制檯應用程序。由於線程共享代理集合(其他線程僅添加Stuff)和一個while循環稍微多了一點,但它非常容易: C# Console App + Event Handling

ThreadPool和Thread Classes。它們一直和BackgroundWorker一樣(自.NET 2.0開始),似乎是爲了解決Console的問題而設計的。你仍然需要用循環來阻止你的主線程。 ThreadPool還會將線程數限制爲Real機器上的某些套件。您應該避免固定的線程限制,而是依靠OS的ThreadPooling功能來處理「多少線程應該同時運行?」的問題。例如,固定數量的5個線程可能是您的6核心開發機器的理想選擇。但對於單核臺式計算機來說太多了。而且在服務器上將會是一個毫無意義的瓶頸,它可以輕鬆地在16到64個內核之間進行備份,並且可以使用GiB的內存。

如果你有.NET 4.0或更高版本,新添加的任務Paralellism可能是值得一試。 http://msdn.microsoft.com/en-us/library/dd537609.aspx 它內置了ThreadPooling,有限的優先化能力和CAN鏈,並加入多重任務輕鬆(一切線程可以做,任務可以做)。 使用任務也允許使用異步和等待關鍵詞: http://msdn.microsoft.com/en-us/library/hh191443.aspx 同樣,你沒有得到解決堵在一個控制檯應用程序的主線程。

+0

事實上,一個事件是非常容易使用和一種高效的方式來保持一個控制檯程序執行,直到發生特定事件。 – user34660 2016-04-04 17:56:29

相關問題