標籤:必須 訊息傳遞 通過 需要 時間 出現 mmu 允許 運用
一。並發&並行
一個應用程式 ---> 一個進程 ---> 運行在自己記憶體位址空間裡的獨立執行體 ---> 同一個記憶體位址空間的一起工作的多個線程
一個並發程式 ---> 多個線程來執行任務 ---> 某個時間點同時運行在多核或者多處理器 ---> 並發&並行
---> 某個時間點同時運行在單個處理器 --\---> 並發&不並行
並行是一種通過使用多處理器以提高速度的能力。所以並發程式可以是並行的,也可以不是。
公認的,使用多線程的應用難以做到準確,最主要的問題是記憶體中的資料共用,它們會被多線程以無法預知的方式進行操作,導致一些無法重現或者隨機的結果(稱作 競態
)
並發方式:
1.確定性(明確定義排序)
2.非確定性(加鎖/互斥從而未定義排序)---> 競態
不要使用全域變數或者共用記憶體,它們會給你的代碼在並發運算的時候帶來危險。
解決之道:
1.同步不同的線程,對資料加鎖,這樣同時就只有一個線程可以變更資料
2.有個被稱作 Communicating Sequential Processes(順序通訊處理)
(CSP, C. Hoare 發明的)
3.還有一個叫做 message passing-model(訊息傳遞)
(已經運用在了其他語言中,比如 Erlang)
二。go的協程
在 Go 中,應用程式並發處理的部分被稱作 goroutines(協程)
協程 ---> 工作在相同的地址空間 ---> 共用記憶體的方式一定是同步的;這個可以使用 sync
包來實現(不推薦)
---> 使用 channels
來同步協程
特點:
1.使用少量的記憶體和資源:使用 4K 的棧記憶體就可以在堆中建立它們
2.對棧進行了分割,從而動態增加(或縮減)記憶體的使用;棧的管理是自動的,但不是由記憶體回收行程管理的,而是在協程退出後自動釋放
3.協程可以運行在多個作業系統線程之間,也可以運行在線程之內
4.使用少量的作業系統線程就能擁有任意多個提供服務的協程,而且 Go 運行時可以聰明的意識到哪些協程被阻塞了,暫時擱置它們並處理其他協程
5.存在兩種並發方式:確定性(明確定義排序)和非確定性(加鎖/互斥從而未定義排序)。Go 的協程和通道理所當然的支援確定性並發方式(例如通道具有一個 sender 和一個 receiver)
實現形式:
關鍵字 go
調用 ---> 一個函數或者方法 ---> 在當前的計算過程中開始一個同時進行的函數 ---> 在相同的地址空間中並且分配了獨立的棧(棧分割)
協程的棧會根據需要進行伸縮,不出現棧溢出;開發人員不需要關心棧的大小。當協程結束的時候,它會靜默退出:用來啟動這個協程的函數不會得到任何的傳回值
三。go協程並行:
Go 預設沒有並行指令,只有一個獨立的核心或處理器被專門用於 Go 程式,不論它啟動了多少個協程;所以這些協程是並發啟動並執行,但他們不是並行啟動並執行:同一時間只有一個協程會處在運行狀態
在 gc 編譯器下(6g 或者 8g)你必須設定 GOMAXPROCS 為一個大於預設值 1 的數值來允許運行時支援使用多於 1 個的作業系統線程,所有的協程都會共用同一個線程除非將 GOMAXPROCS 設定為一個大於 1 的數。當 GOMAXPROCS 大於 1 時,會有一個線程池管理許多的線程。通過 gccgo
編譯器 GOMAXPROCS 有效與運行中的協程數量相等。假設 n 是機器上處理器或者核心的數量。如果你設定環境變數 GOMAXPROCS>=n,或者執行 runtime.GOMAXPROCS(n)
,接下來協程會被分割(分散)到 n 個處理器上。更多的處理器並不意味著效能的線性提升。有這樣一個經驗法則,對於 n 個核心的情況設定 GOMAXPROCS 為 n-1 以獲得最佳效能,也同樣需要遵守這條規則:協程的數量 > 1 + GOMAXPROCS > 1。
所以如果在某一時間只有一個協程在執行,不要設定 GOMAXPROCS!
還有一些通過實驗觀察到的現象:在一台 1 顆 CPU 的膝上型電腦上,增加 GOMAXPROCS 到 9 會帶來效能提升。在一台 32 核的機器上,設定 GOMAXPROCS=8 會達到最好的效能,在測試環境中,更高的數值無法提升效能。如果設定一個很大的 GOMAXPROCS 只會帶來輕微的效能下降;設定 GOMAXPROCS=100,使用 top
命令和 H
選項查看到只有 7 個活動的線程。
增加 GOMAXPROCS 的數值對程式進行並發計算是有好處的;
總結:GOMAXPROCS 等同於(並發的)線程數量,在一台核心數多於1個的機器上,會儘可能有等同於核心數的線程在並行運行。
go協程