科技

重學併發程式設計---併發模型之流水線模式

第二種併發模型我們稱之為流水線併發模型。我之所以選用這個名字,只是為了配合“並行工作者”的隱喻。其他開發者可能會根據平臺或社群選擇其他稱呼(比如說反應器系統,或事件驅動系統)。

下圖表示一個流水線併發模型:

類似於工廠中生產線上的工人們那樣組織工作者。每個工作者只負責作業中的部分工作。當完成了自己的這部分工作時工作者會將作業轉發給下一個工作者。每個工作者在自己的執行緒中執行,並且不會和其他工作者共享狀態。有時也被稱為無共享並行模型。

通常使用非阻塞的 IO 來設計使用流水線併發模型的系統。非阻塞 IO 意味著,一旦某個工作者開始一個 IO 操作的時候(比如讀取檔案或從網路連線中讀取資料),這個工作者不會一直等待 IO 操作的結束。IO 操作速度很慢,所以等待 IO 操作結束很浪費 CPU 時間。此時 CPU 可以做一些其他事情。當 IO 操作完成的時候,IO 操作的結果(比如讀出的資料或者資料寫完的狀態)被傳遞給下一個工作者。

有了非阻塞 IO,就可以使用 IO 操作確定工作者之間的邊界。工作者會盡可能多執行直到遇到並啟動一個 IO 操作。然後交出作業的控制權。當 IO 操作完成的時候,在流水線上的下一個工作者繼續進行操作,直到它也遇到並啟動一個 IO 操作。

在實際應用中,作業有可能不會沿著單一流水線進行。由於大多數系統可以執行多個作業,作業從一個工作者流向另一個工作者取決於作業需要做的工作。在實際中可能會有多個不同的虛擬流水線同時執行。這是現實當中作業在流水線系統中可能的移動情況:

作業甚至也有可能被轉發到超過一個工作者上併發處理。比如說,作業有可能被同時轉發到作業執行器和作業日誌器。下圖說明了三條流水線是如何通過將作業轉發給同一個工作者(中間流水線的最後一個工作者)來完成作業:

流水線有時候比這個情況更加複雜。

反應器,事件驅動系統

採用流水線併發模型的系統有時候也稱為反應器系統或事件驅動系統。系統內的工作者對系統內出現的事件做出反應,這些事件也有可能來自於外部世界或者發自其他工作者。事件可以是傳入的 HTTP 請求,也可以是某個檔案成功載入到記憶體中等。

現在,已經有很多有趣的反應器/事件驅動平臺可以使用了,並且不久的將來會有更多。

比較流行的似乎是這幾個:

Vert.x AKKa Node.JS(JavaScript) 我個人覺得 Vert.x 是相當有趣的(特別是對於我這樣使用 Java/JVM 的人來說)

Actors 和 Channels

Actors 和 channels 是兩種比較類似的流水線(或反應器/事件驅動)模型。

在 Actor 模型中每個工作者被稱為 actor。Actor 之間可以直接非同步地傳送和處理訊息。Actor 可以被用來實現一個或多個像前文描述的那樣的作業處理流水線。

下圖給出了 Actor 模型:

而在 Channel 模型中,工作者之間不直接進行通訊。相反,它們在不同的通道中釋出自己的訊息(事件)。其他工作者們可以在這些通道上監聽訊息,傳送者無需知道誰在監聽。

下圖給出了 Channel 模型:

現在,channel 模型對於我來說似乎更加靈活。一個工作者無需知道誰在後面的流水線上處理作 業。只需知道作業(或訊息等)需要轉發給哪個通道。通道上的監聽者可以隨意訂閱或者取消訂閱,並不會影響向這個通道傳送訊息的工作者。這使得工作者之間具有鬆散的耦合。

流水線模型的優點

一.無需共享的狀態

工作者之間無需共享狀態,意味著實現的時候無需考慮所有因併發訪問共享物件而產生的併發性問題。這使得在實現工作者的時候變得非常容易。在實現工作者的時候就好像是單個執行緒在處理工作-基本上是一個單執行緒的實現。

二.有狀態的工作者

當工作者知道了沒有其他執行緒可以修改它們的資料,工作者可以變成有狀態的。對於有狀態,我是指,它們可以在記憶體中儲存它們需要操作的資料,只需在最後將更改寫回到外部儲存系統。因此,有狀態的工作者通常比無狀態的工作者具有更高的效能。

三.較好的硬體整合(Hardware Conformity)

單執行緒程式碼在整合底層硬體的時候往往具有更好的優勢。首先,當能確定程式碼只在單執行緒模式下執行的時候,通常能夠建立更優化的資料結構和演算法。

其次,像前文描述的那樣,單執行緒有狀態的工作者能夠在記憶體中快取資料。在記憶體中快取資料的同時,也意味著資料很有可能也快取在執行這個執行緒的 CPU 的快取中。這使得訪問快取的資料變得更快。

我說的硬體整合是指,以某種方式編寫的程式碼,使得能夠自然地受益於底層硬體的工作原理。有些開發者稱之為 mechanical sympathy。我更傾向於硬體整合這個術語,因為計算機只有很少的機械部件,並且能夠隱喻“更好的匹配(match better)”,相比“同情(sympathy)”這個詞在上下文中的意思,我覺得“conform”這個詞表達的非常好。當然了,這裡有點吹毛求疵了,用自己喜歡的術語就行。

四.合理的作業順序

基於流水線併發模型實現的併發系統,在某種程度上是有可能保證作業的順序的。作業的有序性使得它更容易地推出系統在某個特定時間點的狀態。更進一步,你可以將所有到達的作業寫入到日誌中去。一旦這個系統的某一部分掛掉了,該日誌就可以用來重頭開始重建系統當時的狀態。按照特定的順序將作業寫入日誌,並按這個順序作為有保障的作業順序。下圖展示了一種可能的設計:

實現一個有保障的作業順序是不容易的,但往往是可行的。如果可以,它將大大簡化一些任務,例如備份、資料 恢復、資料複製等,這些都可以通過日誌檔案來完成。

流水線模型的缺點

流水線併發模型最大的缺點是作業的執行往往分佈到多個工作者上,並因此分佈到專案中的多個類上。這樣導致在追蹤某個作業到底被什麼程式碼執行時變得困難。

同樣,這也加大了程式碼編寫的難度。有時會將工作者的程式碼寫成回撥處理的形式。若在程式碼中嵌入過多的回撥處理,往往會出現所謂的回撥地獄(callback hell)現象。所謂回撥地獄,就是意味著在追蹤程式碼在回撥過程中到底做了什麼,以及確保每個回撥只訪問它需要的資料的時候,變得非常困難.

使用並行工作者模型可以簡化這個問題。你可以開啟工作者的程式碼,從頭到尾優美的閱讀被執行的程式碼。當然並行工作者模式的程式碼也可能同樣分佈在不同的類中,但往往也能夠很容易的從程式碼中分析執行的順序。

Reference:科技日報

看更多!請加入我們的粉絲團

轉載請附文章網址

不可錯過的話題