2015-11-02 132 views
1

編輯:這個問題可能適用於其他語言 - 背後的整體理論似乎主要是語言不可知的。但是,因爲這將在JVM中運行,所以我確定JVM開銷/線程與其他環境之間存在差異。爲了澄清一點,我想主要的問題是哪個更適合可伸縮性:具有更小的線程可以更快地返回,以便爲其他工作負載處理其他工作塊,或嘗試獲取儘可能快地完成單個工作量?在這種情況下,工作負載是連續的,多線程無助於加速單個工作單元;它更希望提高整個系統的吞吐量(感謝Uri帶領我澄清)。Java多線程 - 更少的線程或更少的線程做更多的線程?

我正在開發一個替換現有系統的系統;目前的系統負載相當沉重,所以我們已經知道替換需要高度可擴展性。它與幾個外部進程進行通信,如電子郵件,其他服務,數據庫等,我已經計劃使它成爲多線程來幫助擴展。我以前在多線程應用程序上工作過,沒有任何性能/可伸縮性需求的高點,所以我沒有太多經驗來獲得絕對最大的併發性。

我的問題是在線程之間分配工作的最好方法是什麼?我在看兩個不同的版本,一個爲完整的工作流創建一個線程,另一個爲每個單獨的步驟創建一個線程,繼續下一步(在新的/不同的線程中)步驟完成 - 可能使用NodeJS風格的回調系統,但不直接關注直接實現細節。

我不太瞭解多線程的基本細節 - 例如上下文切換等,所以我不知道多線程的開銷是否會縮短每個線程的執行時間。一方面,與多線程相比,單線程模型似乎對單個工作流程來說是最快的;然而,它也會爲整個工作流程綁定一個線程,而多線程會更短,並且會更快地返回池(至少我想)。

希望底層的概念很容易理解;這裏是一個人爲的僞代碼示例,但:

// Single-thread approach 
foo(); 
bar(); 
baz(); 

或者:

// Multiple Thread approach 
Thread.run(foo); 
when foo.isDone() 
    Thread.run(bar); 
    when bar.isDone() 
     Thread.run(baz); 

UPDATE:完全忘了。我正在考慮多線程方法的原因是(可能錯誤地)認爲,由於線程執行時間較短,因此它們可用於整個工作負載的其他實例。如果每個操作需要5秒鐘,那麼單線程版本會鎖定一個線程15秒;多線程版本會鎖定單個線程5秒鐘,然後它可以用於另一個進程。

任何想法?如果在網頁中有任何類似的內容,我甚至會喜歡這個鏈接 - 我想不出如何搜索這個(我爲此責怪週一,但明天可能會是相同的)。

+6

如果你分拆新主題只有有父線程等待子線程完成,究竟是你獲得? – Powerlord

+0

你是對的 - 我的意思是要覆蓋,並完全跳過它。我會更新。 – MCory

+1

如果您希望事情運行得更快,請將同步而不是順序任務分解爲單獨的線程。 –

回答

1

如果您需要先執行foo,然後再執行bar,然後執行baz,則應該有一個線程按順序執行這些步驟中的每一個。這很簡單,很明顯。

最常見的情況下你是用流水線的方式最好是保持在高速緩存中的代碼比將數據保持在高速緩存更重要的時候。在這種情況下,讓一個反覆執行foo的線程可以將此步驟的代碼保留在緩存中,保持分支預測信息等等。但是,將foo的結果傳遞給bar的線程時,將會出現數據高速緩存未命中的情況。

這是比較複雜的,如果你有充分的理由認爲這將更好地工作,只應嘗試。

+0

我大體上同意,但我要指出的是,試圖保持在高速緩存中的代碼將複雜得多,問題之擬議例子只是每個工作分解成幾部分組成。這個問題的例子仍然會隨機地將這些部分分配給處理器,並且可能會增加而不是減少緩存未命中。 –

2

多線程是不是銀彈。這意味着結束。 在進行任何更改之前,您需要問自己瓶頸的位置以及您真正想要並行化的內容。我不確定沒有更多的信息,我們可以在這裏給出好的建議。

如果富,酒吧和巴茲是管道的一部分,你不一定要去通過使用多線程來提高單個序列的整體延遲。

你也許能夠做的就是通過讓管道在不同的輸入塊並行工作的多個執行,通過讓後來的項目通過管道向行駛,而較早的項目被阻塞的東西(例如,增加你的吞吐量, I/O)。例如,如果特定輸入的bar()被阻塞並等待通知,那麼可能會對另一個輸入執行計算量大的操作,或者將CPU資源投入到foo()中。一個特別重要的問題是,是否有任何外部依賴作爲有限的共享資源。例如,如果一個線程正在訪問系統X,另一個線程是否會受到影響?如果你想分而治之的問題

線程也是非常有效的 - 分裂的投入更小的部分,通過管道運行的每個部分,然後在所有的作品等着準備。這是可能的,你正在尋找哪種工作流程?

+0

我認爲你對增加吞吐量的評論是我的目標。保持這個例子,如果3次調用每次需要5秒,那麼至少需要15秒來處理單個輸入。由於它是連續的,所以我無能爲力。但會分解成多個線程使服務能夠處理更多的請求比佔用單個線程的全15秒更容易,或者它會更好,只是專注於獲得個人項目儘快出? – MCory

+0

如果你有快速和慢速項的組合,那麼它可能是有意義的以後「放下」大工作項目和「接他們回來了。」但是,如果所有的工作都是大致相同的工作量,那麼一旦開始工作就可能沒有意義。 –

+0

@Mcory:是否有任何步驟在等待外部事件時被阻止?並且對同一資源執行額外的併發請求會減慢或中斷第一個請求? 如果你的計算是在CPU負載方面只是重,你的機器是不是真的閒着,所以額外的線程不會使吞吐量不一定更好。另一方面,如果請求正在等待通知,那麼只要讓每個輸入通過不同線程的管道就足夠了。 – Uri

0

對整個工作流使用單線程。

劃分工作流並不能改善一件作品的完成時間:由於工作流的各個部分必須按順序完成,因此一次只能有一個線程處理該部分工作。然而,分解階段可能會延遲一件作品的完成時間,因爲可能拾起一件作品最後部分的處理器可能會取代另一件作品的第一部分。

將階段拆分爲多個線程也不太可能改善完成所有工作的時間,與執行一個線程中的所有階段相關,因爲最終您仍然必須執行所有階段的所有階段工作。

下面是一個例子。如果您有200個這樣的工作,每個工作需要三個5秒的階段,並且說兩個線程在兩個處理器上運行,那麼將整個工作流保持在單個線程中會在15秒後得到前兩個結果。獲得所有結果需要1500秒,但您一次只需要工作記憶中的兩項工作。如果分解了各個階段,那麼獲得第一個結果可能需要15秒以上的時間,如果您仍想在1500秒內獲得所有結果,則可能需要並行處理所有200個工作的內存。

在大多數情況下,有沒有效率優勢,打破了連續的階段分成不同的線程,並有可能成爲明顯的缺點。線程通常只在您可以使用它們並行工作時纔有用,這似乎並不適用於您的工作階段。

但是,將階段拆分爲單獨的線程存在巨大的缺點。缺點是您現在需要編寫管理階段的多線程代碼。在這樣的代碼中編寫錯誤非常容易,而且在生產部署之前可能很難捕捉到這些錯誤。

避免此類錯誤的方法是根據您的要求儘可能簡化線程代碼。在你的工作階段,最簡單的線程代碼根本就沒有。