2011-07-27 66 views
2

我正在開發一個小型多線程項目。系統可以分爲2個子部分,A和B.數據從A流向B.
部分保持原生來自外部世界的數據,做一些轉換,然後生成數以千計的新數據,我們稱之爲A_OUTPUT。
B部分根據每個A_OUTPUT做一些計算然後生成更多的數據,可能是A_OUTPUT數量的十倍。生產者 - 消費者問題中的設計問題

我很困惑如何同步2個部分。
我自己的設計是創建一個工作隊列以及一個保護兩個子部件之間隊列的鎖。還要創建一個事件來指示工作隊列是否爲空。

一個部分由多個線程組成,每個線程從外部獲取數據並生成A_OUTPUT,每次單個A線程生成一個A_OUTPUT時,線程獲取隊列鎖,將A_OUTPUT推入隊列,釋放鎖,然後觸發事件。

B部分包含主管線程和多個工作線程,主管線程首先被阻塞在事件上。事件觸發後,主管線程鎖定隊列,獲取隊列的所有A_OUTPUTS,釋放鎖,將A_OUTPUTS分派給工作線程,然後再次等待事件。

這種設計的問題是顯而易見的,B的主管線將與多線程贏得了隊列鎖賽車。也許當B終於擁有這個鎖時,隊列中已經有10個或更多的A_OUTPUT了,最老的A_OUTPUT很久以前就已經生成了。我希望儘可能快地處理每個A_OUTPUT。

我知道我可以劃分工作隊列分成幾個較小的隊列或添加較多的B主管線程注入鎖定戰鬥,縮短平均時間每A_OUTPUT等待它得到處理之前。但是可能存在更合適的設計?

而另一個問題,確實存在着對不同目的的多線程程序的任何模式或設計模式?

+0

這裏涉及2個概念:隊列鎖定和同步。我不熟練使用C++,但java在處理這個隊列對象中處理這兩個概念,名爲'Blocking Queue':http://download.oracle.com/javase/1.5.0/docs/api/java/ util/concurrent/BlockingQueue.html – DanC

+0

你打算如何控制創建的B工作線程的數量,不可能只是失去控制。 「獲取隊列的所有A_OUTPUTS」聽起來不是一個好主意,因爲如果處理A_OUTPUTS需要時間,因爲我們正在研究無限制的線程創建。我認爲我們在這裏需要某種線程池。 –

回答

3

這毋寧說是在wikipedia

很好的描述,我可以推薦以下方法經典問題:由mutex

同步訪問隊列。保留兩個condition variables,一個表示隊列未滿(表示隊列未滿),另一個表示隊列中有任何數據,則需要處理Producer產生的數據量多於Consumer可能消耗的數據。

Producer檢查隊列是否已滿。如果滿 - 等待條件「不滿」,否則產生一些數據,將其放入隊列中,通知「有數據」狀態。

Consumer檢查,如果隊列中有任何數據,消耗它,並通知「未滿」條件

您也可以使用無鎖隊列獲得更好的性能。請檢查TBB或最近宣佈的Boost.Lockless(此刻正在審覈中)。順便說一句,使用TBB的整個任務是非常簡單的,只需使用他們的調度員和容器,並忘記了 顯式同步

0

您可以通過限制隊列大小來控制隊列時限問題,就像Andy T建議的一樣。

此外,如果您放棄主管線程,您的佈局變得更加簡單。讓A的線程在完成處理時獲取互斥量,將它們的項目放入隊列中,並等待隊列足夠空以繼續。每個消費者線程都會等待,直到隊列中的項目可用,獲取並使用它。

由於A和B的線程數足夠接近,所以飢餓應該不太可能。另外,你應該使用像pthread_cond_signal(而不是pthread_cond_broadcast)這樣的方法,它只喚醒每一邊的單個線程,最大限度地減少比賽的數量。

1

像@thiton,我沒有看到需要一個主管線程 - 它看起來像一個不必要的複雜?

這似乎是一個流量控制和排隊設計的問題。鑑於你似乎需要,我會去兩個生產者 - 消費者隊列和有限數量的A_OUTPUT實例。我會創建1000個(說)A_OUTPUT實例,並將它們推送到一個P-C隊列中,在啓動時形成一個線程安全對象池(池隊列)。 A線程開始,從池中彈出A_OUTPUT並開始'從外部世界獲取原始數據'。當A線程在其A_OUTPUT中獲得數據時,它將其推送到另一個P-C隊列(通信隊列)。 B線程池正在等待通信隊列。當A_OUTPUT在通信隊列中變爲可用時,B線程將獲取並處理它。當B線程完成數據時,將A_OUTPUT推回池隊列。 A_OUTPUT實例因此循環傳送,將數據從系統的一端傳送到另一端,然後通過池隊列返回到起點。

像這樣的設計允許跨多個線程/池的流量控制。隊列中有足夠的'鬆弛'來允許高負載突發,但失控的線程/對象是不可能的 - 如果有太多數據流過,A線程將會找到空池並阻塞它,直到A_OUTPUT實例變爲可用 - 當是時,A線程將繼續獲取更多數據。

這樣的系統可以在運行時進行調整。從A/B線程池添加/刪除線程以及增加/減少對象池深度很容易。

哦 - 你不需要複雜的有界P-C隊列。如果每個隊列都可以容納池中的對象數量,那就足夠了 - 再也不會有更多的對象可用。

RGDS, 馬丁