2013-05-06 55 views
0

需要關於c#3.0中的多線程最佳方法的建議(無並行或任務)如何維護固定的線程集合?

情況是,我有一個包含500個項目的隊列。在特定的時間,我只能運行10個線程(最大)。以下是我的代碼。

While (queue.Count > 0) 
{ 
Thread[] threads = new Thread[no_of_threads]; 
for (int j = 0; j < no_of_threads; j++) 
{ 
    threads[j] = new Thread(StartProcessing);//StartProcessing Dequeue one item each time //for a single thread 
    threads[j].Start(); 
} 

foreach (Thread objThread in threads) 
{ 
    objThread.Join(); 
} 
} 

問題在這種方法中,對於一個實例,如果no_of_threads = 10,並從他們的9線與處理進行,1個線程仍在工作,我不能來循環出來工作委託給了直到所有10個線程都完成爲止。

我需要在所有的時間10個線程應該工作,直到隊列計數> 0

+0

TPL在.NET 3.5中可用。 [.NET 3.5的任務並行庫](http://nuget.org/packages/TaskParallelLibrary/) – Romoku 2013-05-06 16:47:21

+0

無論如何,創建1個線程來取消1個項目並不是非常有效。沒有必要Join()在這裏,想一想更好的控制流程。 – 2013-05-06 16:53:24

回答

4

這很容易通過Semaphore完成。

這個想法是創建一個最大計數爲N的信號量,其中N是允許的線程數。循環在信號量上等待,並在獲取信號量時排隊任務。

Semaphore ThreadsAvailable = new Semaphore(10, 10); 
while (Queue.Count > 0) 
{ 
    ThreadsAvailable.WaitOne(); 
    // Must dequeue item here, otherwise you could run off the end of the queue 
    ThreadPool.QueueUserWorkItem(DoStuff, Queue.Dequeue()); 
} 

// Wait for remaining threads to finish 
int threadCount = 10; 
while (threadCount != 0) 
{ 
    ThreadsAvailable.WaitOne(); 
    --threadCount; 
} 


void DoStuff(object item) 
{ 
    ItemType theItem = (ItemType)item; 
    // process the item 
    StartProcessing(item); 
    // And then release the semaphore so another thread can run 
    ThreadsAvailable.Release(); 
} 

該項目在主循環中出隊,因爲這樣可以避免競爭條件,否則這些條件很難處理。如果讓線程出列的項目,那麼線程必須做到這一點:

lock (queue) 
{ 
    if (queue.Count > 0) 
     item = queue.Dequeue(); 
    else 
     // There wasn't an item to dequeue 
     return; 
} 

否則,事件將按照下列順序可能是當只有一個留在隊列項出現。

main loop checks Queue.Count, which returns 1 
main loop calls QueueUserWorkItem 
main loop checks Queue.Count again, which returns 1 because the thread hasn't started yet 
new thread starts and dequeues an item 
main loop tries to dequeue an item and throws an exception because queue.Count == 0 

如果你願意這樣處理事情,那麼你沒事。關鍵是在線程退出之前確保線程在信號量上調用Release。您可以使用顯式託管的線程或使用我發佈的ThreadPool方法來完成此操作。我只是使用ThreadPool,因爲我覺得比顯式管理線程更容易。

+2

我認爲它應該是'while(--threadCount!= 0)' – Romoku 2013-05-06 17:27:25

+0

感謝您的方法,更多的疑問。我需要從隊列中選擇傳遞給DoStuff的項目。爲此,我需要鎖定隊列以確保相同的項目不會被多個線程從隊列中拾取,那麼在ThreadPool的情況下如何實現? – SAM 2013-05-06 17:58:32

+0

@Romoku:謝謝。糾正。 – 2013-05-06 18:19:51

0

這是一個簡單的生產者 - 消費者方案。您需要一個像這樣的線程安全隊列:Creating a blocking Queue<T> in .NET? - 10個線程可以按循環中的作業讀取和處理作業,直到隊列爲空。 根據您填入隊列的方式(在開始處理隊列或處理隊列之前),您可以在隊列變空或者通過停止標誌指示停止隊列時立即終止這些線程。在後一種情況下,您可能需要喚醒線程(例如虛擬作業)。

+0

BlockingCollection在c#3.0中不可用。它增加了4.0。謝天謝地,這使得這個問題更容易處理。 – cgotberg 2013-05-06 16:33:56

+0

BlockingCollection在3.0中不存在 – Servy 2013-05-06 16:33:59

+0

是的,我意識到這太遲了,並且即將編輯我的帖子。 – JeffRSon 2013-05-06 16:34:58

0

而不是從外面控制線程,讓每個線程本身消費數據。

僞代碼:

create 10 threads 

thread code: 
    while elements in queue 
    get element from queue 
    process element 
+0

請注意,您必須確保您正確同步隊列的訪問權限,因爲.NET 3.0中沒有'ConcurrentQueue' – Servy 2013-05-06 16:34:51

1

您應該使用ThreadPool管理併爲您

一旦在池中的線​​程完成了它的任務,它返回到等待線程隊列優化線程,它可以重複使用。這種重用使應用程序可以避免爲每個任務創建新線程的成本。

線程池通常具有最大數量的線程。如果所有線程都處於忙碌狀態,則會將其他任務放入隊列中,直到它們可以在線程可用時進行服務。

最好不要干預ThreadPool,因爲它足夠聰明地管理和分配線程。但是如果你真的需要這樣做,你可以通過使用SetMaxThreads方法來設置最大線程數的約束。方法

+0

好的,但是如何將它限制爲10個線程,以及如何在每個線程完成時添加新線程?沒有額外的指導,我認爲OP已經擁有了這個線程數組。 – 2013-05-06 16:33:42

+0

@RobertHarvey - 我猜這裏的想法是,由於性能原因,OP一次限制爲10個線程。 ThreadPool將完成這項工作並優化所有內容,而不是OP必須自己管理它。 – 2013-05-06 16:35:12

+1

@JustinNiessner雖然有時對線程數量的限制只是OP要求他們認爲最好的,線程池會更聰明,有時候確實有更多的線程指定數量,有時是因爲你知道更多線程池確實有時是因爲線程池正在被程序的另一個方面使用,並且您需要分離這些池。 – Servy 2013-05-06 16:36:48

1

所以你需要處理的是一個被設計爲可以從多線程線程訪問的隊列。你使用.NET 4.0我會說使用BlockingCollection。它不僅能夠完美工作,而且非常高效。你可以在所有的方法中調用你自己的類,它只是一個Queue調用lock。它也會起作用,但效率不高。 (這可能是有效的足夠你的目的雖然和重新編寫BlockingCollection「正常」將是相當困難的。)

一旦你的隊列中的每個工人可以隨意在該隊列中的項目,過程它,然後問隊列另一個。當沒有更多的時候,你不必擔心結束該線程;沒有更多的工作可以做。

+0

實際上,隊列只保存一些batch_id,基於該ID,我需要做一些處理(比如說從圖像生成文檔)。我正在使用C#3.0,我相信這個功能是不可用的..無論如何感謝您的幫助,我一定會探討BlockingCollection。 – SAM 2013-05-06 20:44:58

+0

@SAM正如我在我的文章中所說的,您無法訪問'BlockingCollection',但您可以通過使用'Queue'和一些您可以訪問的鎖來模擬效果。它不會那麼高效,但效率會很高,而且會起作用。 – Servy 2013-05-06 20:46:04

+0

實際上工作得很好。您啓動N個線程並讓它們一直到隊列爲空。 – 2013-05-06 22:13:30