這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
1. 作業系統與運行庫
“運行庫”這個詞其實不止包括用於和編譯後的目標執行程式進行連結的庫檔案,也包括了指令碼語言或位元組碼解釋型語言的運行環境,譬如Python,C#的CLR,Java的JRE。
對系統調用的封裝只是運行庫的很小一部分功能,運行庫通常還提供了諸如字串處理、數學計算、常用資料結構容器等等不需要作業系統支援的功能,同時,運行庫也會對作業系統支援的功能提供更易用更進階的封裝,譬如帶緩衝和格式的IO、線程池。
1. 支援新的語義或文法,從而便於我們描述和解決問題。譬如Java的泛型、Annotation、lambda運算式。
2. 提供了新的工具或類庫,減少了我們開發的代碼量。譬如Python 2.7的argparse
3. 對系統調用有了更良好更全面的封裝,使我們可以做到以前在這個語言環境裡做不到或很難做到的事情。譬如Java NIO
2. 並發與並行 (Concurrency and Parallelism)
並發是指程式的邏輯結構。非並發的程式只有一個邏輯控制流程,即順序執行的(Sequential)程式。
並行是指程式的運行狀態。如果一個程式在某一時刻被多個CPU流水線同時進行處理,那麼我們就說這個程式是以並行的形式在運行。
3. 線程的調度
4. 並發編程架構
5. goroutine
(1) goroutine是Go語言運行庫的功能,不是作業系統提供的功能,goroutine不是用線程實現的。具體可參見Go語言源碼裡的http://golang.org/src/pkg/runtime/proc.c
(2) goroutine就是一段代碼,一個函數入口,以及在堆上為其分配的一個堆棧。所以它非常廉價,我們可以很輕鬆的建立上萬個goroutine,但它們並不是被作業系統所調度執行
(3) 除了被系統調用阻塞的線程外,Go運行庫最多會啟動$GOMAXPROCS個線程來運行goroutine
(4) goroutine是協作式調度的,如果goroutine會執行很長時間,而且不是通過等待讀取或寫入channel的資料來同步的話,就需要主動調用Gosched()來讓出CPU
(5) 和所有其他並發架構裡的協程一樣,goroutine裡所謂“無鎖”的優點只在單線程下有效,如果$GOMAXPROCS > 1並且協程間需要通訊,Go運行庫會負責加鎖保護資料,這也是為什麼sieve.go這樣的例子在多CPU多線程時反而更慢的原因
(6) Web等服務端程式要處理的請求從本質上來講是平行處理的問題,每個請求基本獨立,互不依賴,幾乎沒有資料互動,這不是一個並發編程的模型,而並發編程架構只是解決了其語義表述的複雜性,並不是從根本上提高處理的效率,也許是並發串連和並發編程的英文都是concurrent吧,很容易產生“並發編程架構和coroutine可以高效處理大量並發串連”的誤解。
(7) Go語言運行庫封裝了非同步IO,所以可以寫出貌似並發數很多的服務端,可即使我們通過調整$GOMAXPROCS來充分利用多核CPU平行處理,其效率也不如我們利用IO事件驅動設計的、按照事務類型劃分好合適比例的線程池。在回應時間上,協作式調度是硬傷。
(8) goroutine最大的價值是其實現了並發協程和實際並存執行的線程的映射以及動態擴充,隨著其運行庫的不斷髮展和完善,其效能一定會越來越好,尤其是在CPU核心數越來越多的未來,終有一天我們會為了代碼的簡潔和可維護性而放棄那一點點效能的差別。