基於執行緒與基於事件的併發程式設計之爭

banq發表於2014-12-15
Java和Node.js可以說分別是基於執行緒和基於事件的兩個併發程式設計代表,它們互相指責瞧不起對方,讓我們看看各種陣營的聲音:

基於事件的粉絲認為執行緒是一個壞主意,原因有是:
1. 你得顯式的協調共享資料的鎖,如果你忘記加鎖,你就會得到中斷壞的資料。
2. 依賴鎖導致死鎖。
3. 它們難以除錯
4. 回撥並沒有鎖
5. 多執行緒在多核上的效能並不會比每個單核一個執行緒效能更好。

你應當儘可能地避免執行緒,對於GUI和分散式系統或低端伺服器不要用執行緒,只有處理CPU併發時才需要執行緒,如果必須使用執行緒,將執行緒隔離在核心內部,讓大部分程式碼保持單執行緒。

而基於執行緒的粉絲認為Why events are a bad idea,反駁理由是:
1. 很多使用執行緒實現高併發卻沒有獲得好效能?這其實假象,有人建立一個可擴充套件到100,000的執行緒庫,其效能匹配SEDA的基於事件實現的效能。
2.執行緒限制流程控制?執行緒派分析了基於事件的系統, 發現這些應用控制流程模式有三種:call-return, parallel calls和 pipeline. 這些都很容易使用執行緒表達。
3.執行緒同步鎖太重量?協調式多工(協程)能讓執行緒輕量等同於基於事件的併發。
4.執行緒堆疊沒有足夠效率管理活動狀態?不是,一個新的動態增長性堆疊stack模型可以解決這個問題。
5.執行緒會阻止執行時刻進行最佳化排程決定?不是這樣,Lauer 和Needham 都顯示不是這種情況。
執行緒派認為現代伺服器雖然需要併發處理大量的請求,但是程式碼處理每個請求通常是有順序的,我們相信執行緒提供這兩種情況下很好的程式設計抽象。
儘管事件系統在高併發下有很好的效能,但是我們已經證明使用執行緒也會有類似效能(banq注:不過真的需要高手),由於語言提供編譯時的分析使得執行緒簡單,一樣和基於事件系統能實現高併發。

執行緒派的改良代表可以首推Go的Goroutine和Python的coroutine協程,它們解決了直接基於OS執行緒導致執行緒上下文切換時帶來的效能損耗,而且透過排程器保證非堵塞。Goroutine最大的特點能夠讓程式設計師以同步順序程式碼的風格編寫非同步執行,Goroutine=coroutine協程 + user space threads + fibers + greenlets。因為綠色執行緒被封裝在語言的API中,因此相比Node.JS顯式處理非同步IO,GO語言提供了隱式的非同步處理IO,從而使得併發非同步變得簡單。

在GO語言的競爭之下,Node.JS一直探索如何避免回撥陷阱,從Promise 到coroutine/Generators,直至演化到Javascript 7的async函式。從而也可以實現使用熟悉的順序程式設計風格編寫出非同步程式碼,下面是使用JS 7的新的非同步函式:

function* getStockPrice(name) {
    var symbol = yield getStockSymbol(name);
    var price = yield getStockPrice(symbol);
    return price;
}
var result = 
    spawn(getStockPrice.bind(null, "Pfizer"));
result.then(console.log, console.error);

<p class="indent">


Javascript 7主要亮點是在事件機制和非同步程式設計的提升上,這兩點主要體現在:
1. Object.observe使得模型和檢視之間很容易同步。
2. async函式更易於非同步程式設計,能夠實現拉Pull或推Push。

http://www.jdon.com/idea/js/javascript7.html

以Node.js為主的事件派和Go的協程派打得熱乎時,這時有了一種觀點,既然你們兩者都回避各自缺陷表現得不錯,下面是就剩下使用者的愛好和技能選擇了,能不能提供一種語言將這兩者結合在一起?

Haskell倡導者提出透過語言統一多執行緒程式設計和事件程式設計,提供一種Monad函式,其內部封裝了事件和多執行緒抽象,無論你是哪派粉絲,都可以使用這個Monad程式設計。如下圖:A language-based approach to unifying events and threads

[img index=1]

這種觀點得到大多數人的同意,這時被冷落一邊的Scala的Actor模型站出來認為自己的Actor函式屬於這種兩者合一的Monad函式,見這裡

當然,Go的綠色執行緒Goroutine與Actor還是有區別的:Actor模型和CSP模型的區別

縱觀併發程式設計發展,大家都是從IO這個序列化埠入手,神仙過海,各顯神招,從當初Java的NIO 到Node.JS的非同步IO 再到Go的Goroutine以及Netty 以及Actor模型等等,應該說,誰在Socket IO這個戰場上效能和易用性表現得更好,誰就可能在併發程式設計整個領域獲得全勝。


[該貼被banq於2014-12-15 08:57修改過]

相關文章