理解并發、并行的例子
先舉例子來理解這2個概念的區別。
老師讓兩個同學去辦公室談話。如果這兩同學(進程)是并列跨過辦公室門(CPU)的,那么就是并行。如果同學A先進同學B后進入(或者先B后A),或者兩人并列同時進入,但是在辦公室外的路人甲(用戶)看來,同學A和同學B同時都在辦公室內,這是并發。
其實這個例子不合理,因為真正的并行是多核CPU下的概念,但上面這個簡單的例子非常有助于理解。
如果舉例要精確一點,那么大概是這樣的:進辦公室有兩個門(兩CPU),如果兩同學分別從不同的門進入,不管先后性,兩者互相獨立,那么是并行;如果兩同學不管以什么方式進入,在路人甲看來,他兩同時都在辦公室內,就是并發。
我不信到現在還不理解并發和并行。
并發和并行的理論性解釋
為什么操作系統上可以同時運行多個程序而用戶感覺不出來?
這是因為無論是單CPU還是多CPU,操作系統都營造出了可以同時運行多個程序的假象。實際的過程操作系統對進程的調度以及CPU的快速上下文切換實現的:每個進程執行一會就先停下來,然后CPU切換到下個被操作系統調度到的進程上使之運行。因為切換的很快,使得用戶認為操作系統一直在服務自己的程序。
再來解釋并發就容易理解多了。
并發(concurrent)指的是多個程序可以同時運行的現象,更細化的是多進程可以同時運行或者多指令可以同時運行。但這不是重點,在描述并發的時候也不會去扣這種字眼是否精確,并發的重點在于它是一種現象。并發描述的是多進程同時運行的現象。但實際上,對于單核心CPU來說,同一時刻只能運行一個進程。所以,這里的"同時運行"表示的不是真的同一時刻有多個進程運行的現象,這是并行的概念,而是提供一種功能讓用戶看來多個程序同時運行起來了,但實際上這些程序中的進程不是一直霸占CPU的,而是執行一會停一會。
所以,并發和并行的區別就很明顯了。它們雖然都說是"多個進程同時運行",但是它們的"同時"不是一個概念。并行的"同時"是同一時刻可以多個進程在運行(處于running),并發的"同時"是經過上下文快速切換,使得看上去多個進程同時都在運行的現象,是一種OS欺騙用戶的現象。
實際上,當程序中寫下多進程或多線程代碼時,這意味著的是并發而不是并行。并發是因為多進程/多線程都是需要去完成的任務,不并行是因為并行與否由操作系統的調度器決定,可能會讓多個進程/線程被調度到同一個CPU核心上。只不過調度算法會盡量讓不同進程/線程使用不同的CPU核心,所以在實際使用中幾乎總是會并行,但卻不能以100%的角度去保證會并行。也就是說,并行與否程序員無法控制,只能讓操作系統決定。
再次注明,并發是一種現象,之所以能有這種現象的存在,和CPU的多少無關,而是和進程調度以及上下文切換有關的。
理解了概念,再來深入擴展下。
串行、并行和并發
任務描述
如圖:
任務是將左邊的一堆柴全部搬到右邊燒掉,每個任務包括三個過程:取柴,運柴,放柴燒火。
這三個過程分別對應一個函數:
串行模式
串行表示所有任務都一一按先后順序進行。串行意味著必須先裝完一車柴才能運送這車柴,只有運送到了,才能卸下這車柴,并且只有完成了這整個三個步驟,才能進行下一個步驟。
和稍后所解釋的并行相對比,串行是一次只能取得一個任務,并執行這個任務。
假設這堆柴需要運送4次才能運完,那么當寫下的代碼類似于下面這種時,那么就是串行非并發的模式:
get()
carry()
unload()
}
或者,將三個過程的代碼全部集中到一個函數中也是如此:
geting
carrying
unloading
}
for(i=0;i<4;i++){
task()
}
這兩種都是串行的代碼模式。畫圖描述:
并行模式
并行意味著可以同時取得多個任務,并同時去執行所取得的這些任務。并行模式相當于將長長的一條隊列,劃分成了多條短隊列,所以并行縮短了任務隊列的長度。
正如前面所舉的兩同學進辦公室的例子,串行的方式下,必須1個同學進入后第二個同學才進入,隊列長度為2,而并行方式下可以同時進入,隊列長度減半了。
并行的效率從代碼層次上強依賴于多進程/多線程代碼,從硬件角度上則依賴于多核CPU。
對于單進程/單線程,由于只有一個進程/線程在執行,所以盡管同時執行所取得的多個任務,但實際上這個進程/線程是不斷的在多任務之間切換,一會執行一下這個,一會執行一下那個,就像是一個人在不同地方之間來回奔波。所以,單進程/線程的并行,效率比串行更低。
對于多進程/多線程,各進程/線程都可以執行各自所取得的任務,這是真正的并行。
但是,還需要考慮硬件層次上CPU核心數,如果只有單核CPU,那么在硬件角度上這單核CPU一次也只能執行一個任務,上面多進程/多線程的并行也并非真正意義上的并行。只有多核CPU,并且多進程/多線程并行,才是真正意義上的并行。
如下圖,是多進程/多線程(2個工作者)的并行:
并發
并發表示多個任務同時都要執行的現象,更詳細的概念前面已經說面的夠具體了。
其實,很多場景下都會使用并發的概念。比如同時500個http請求涌向了web服務器,比如有時候說并發數是1000等。
有時候也將并發當成任務,比如500并發數意味著500個任務,表示的是在一個特定的時間段內(約定俗成的大家認為是1秒)可以完成500個任務。這500個任務可以是單進程/單線程方式處理的,這時表示的是并發不并行的模式(coroutine就是典型的并發不并行),即先執行完一個任務后才執行另一個任務,也可以是多進程/多線程方式處理的,這時表示的是并發且并行模式。
要解決大并發問題,通常是將大任務分解成多個小任務。很典型的一個例子是處理客戶端的請求任務,這個大任務里面包含了監聽并建立客戶端的連接、處理客戶端的請求、響應客戶端。但基本上所有這類程序,都將這3部分任務分開了:在執行任何一個小任務的時候,都可以通過一些手段使得可以執行其它小任務,比如在處理請求的時候,可以繼續保持監聽狀態。
由于操作系統對進程的調度是隨機的,所以切分成多個小任務后,可能會從任一小任務處執行。這可能會出現一些現象:
- 可能出現一個小任務執行了多次,還沒開始下個任務的情況。這時一般會采用隊列或類似的數據結構來存放各個小任務的成果。比如負責監聽的進程已經執行了多次,建立了多個連接,但是還沒有調度到處理請求的進程去處理任何一個請求。
- 可能出現還沒準備好第一步就執行第二步的可能。這時,一般采用多路復用或異步的方式,比如只有準備好產生了事件通知才執行某個任務。比如還沒有和任何一個客戶端建立連接時,就去執行了處理請求的進程。
- 可以多進程/多線程的方式并行執行這些小任務。也可以單進程/單線程執行這些小任務,這時很可能要配合多路復用才能達到較高的效率
看圖非常容易理解:
上圖中將一個任務中的三個步驟取柴、運柴、卸柴劃分成了獨立的小任務,有取柴的老鼠,有運柴的老鼠,有卸柴燒火的老鼠。
如果上圖中所有的老鼠都是同一只,那么是串行并發的,如果是不同的多只老鼠,那么是并行并發的。
總結
并行和串行:
- 串行:一次只能取得一個任務并執行這一個任務
- 并行:可以同時通過多進程/多線程的方式取得多個任務,并以多進程或多線程的方式同時執行這些任務
- 注意點:
- 如果是單進程/單線程的并行,那么效率比串行更差
- 如果只有單核cpu,多進程并行并沒有提高效率
- 從任務隊列上看,由于同時從隊列中取得多個任務并執行,相當于將一個長任務隊列變成了短隊列
并發:
- 并發是一種現象:同時運行多個程序或多個任務需要被處理的現象
- 這些任務可能是并行執行的,也可能是串行執行的,和CPU核心數無關,是操作系統進程調度和CPU上下文切換達到的結果
- 解決大并發的一個思路是將大任務分解成多個小任務:
- 可能要使用一些數據結構來避免切分成多個小任務帶來的問題
- 可以多進程/多線程并行的方式去執行這些小任務達到高效率
- 或者以單進程/單線程配合多路復用執行這些小任務來達到高效率
-
cpu
+關注
關注
68文章
10825瀏覽量
211150 -
操作系統
+關注
關注
37文章
6738瀏覽量
123190 -
并行
+關注
關注
0文章
80瀏覽量
18830 -
程序
+關注
關注
116文章
3777瀏覽量
80851
發布評論請先 登錄
相關推薦
評論