現代操作系統提供了基于三種基本的構造并發(fā)程序的方法。分別為:進(jìn)程、I/O 多路復用和線(xiàn)程。
基于進(jìn)程的并發(fā)編程方法很簡(jiǎn)單,使用我們很熟悉的fork、exec、waitpid等函數就可以了。比如構造一個(gè)并發(fā)服務(wù)器的方法就是,在父進(jìn)程中接受客戶(hù)端的請求,然后創(chuàng )建一個(gè)新的子進(jìn)程來(lái)為每個(gè)客戶(hù)端提供服務(wù)。
因為進(jìn)程有獨立的地址空間,所以不會(huì )出現進(jìn)程不小心覆蓋另一個(gè)進(jìn)程虛擬內存的情況,這是一個(gè)顯著(zhù)的優(yōu)點(diǎn)。但獨立地址空間也讓不同進(jìn)程之間共享信息變得更加困難。
考慮下面一種場(chǎng)景,服務(wù)器也能對用戶(hù)從標準輸入鍵入的交互命令做出響應,那服務(wù)器就必須響應兩個(gè)相互獨立的 I/O 事件:1)客戶(hù)端連接請求,2)用戶(hù)鍵入命令行。我們無(wú)法選擇應該等待哪個(gè)事件,因為等待其中一個(gè)就不能響應另一個(gè)事件,所以就有了 I/O 多路技術(shù)。
其基本思路就是使用select函數,要求內核掛起進(jìn)程,只有在一個(gè)或多個(gè) I/O 事件發(fā)生后,才將控制返回給應用程序。
該技術(shù)可以用作并發(fā)事件驅動(dòng)程序的基礎,在事件驅動(dòng)程序中某些事件會(huì )導致流向前推進(jìn)(比如 web 前端程序),一般思路是將邏輯流轉換為狀態(tài)機(去查編譯原理),某些事件會(huì )導致從一個(gè)狀態(tài)轉移到另一個(gè)狀態(tài)。
如果使用進(jìn)程并發(fā)實(shí)現上面的場(chǎng)景,那可能就需要兩個(gè)進(jìn)程分別監聽(tīng)兩個(gè)事件,而使用 I/O 多路復用技術(shù)的程序時(shí)運行在單一進(jìn)程上下文中,因此每個(gè)邏輯流都能訪(fǎng)問(wèn)全部該進(jìn)程全部的地址空間,也就是說(shuō)共享數據變得很容易,但是事件驅動(dòng)要比基于進(jìn)程更加復雜,而且隨著(zhù)并發(fā)粒度的減小,復雜性還會(huì )上升。另外如果某個(gè)邏輯流正忙于讀一個(gè)文本行,那么其它邏輯流就不會(huì )有進(jìn)展,這防不住惡意客戶(hù)端的攻擊。
線(xiàn)程同時(shí)結合了前面兩種方法。線(xiàn)程是運行在進(jìn)程上下文的邏輯流,它有自己的棧、棧指針、程序計數器等,但是共享這個(gè)進(jìn)程虛擬空間的所有內容,包括堆、代碼、數據、共享庫及打開(kāi)文件。