并發(fā)編程基礎(chǔ)知識(shí)一 并發(fā)和并行

這里先放結(jié)論:你可以編寫一個(gè)擁有多個(gè)線程或者進(jìn)程的并發(fā)程序,但如果沒有多核處理器來執(zhí)行這個(gè)程序,那么就不能以并行方式來運(yùn)行代碼。并發(fā)和并行都可以是很多個(gè)線程,就看這些線程能不能同時(shí)被(多個(gè))cpu執(zhí)行,如果可以就說明是并行,而并發(fā)是多個(gè)線程被(一個(gè))cpu 輪流切換著執(zhí)行。

參考
詳解并發(fā)和并行意義
并發(fā)與并行的區(qū)別?

一、例一

假設(shè)一個(gè)有三個(gè)學(xué)生需要輔導(dǎo)作業(yè),幫每個(gè)學(xué)生輔導(dǎo)完作業(yè)是一個(gè)任務(wù)

1.順序執(zhí)行:老師甲先幫學(xué)生A輔導(dǎo),輔導(dǎo)完之后再取給B輔導(dǎo),最后再去給C輔導(dǎo),效率低下 ,很久才完成三個(gè)任務(wù)

2.并發(fā):老師甲先給學(xué)生A去講思路,A聽懂了自己書寫過程并且檢查,而甲老師在這期間直接去給B講思路,講完思路再去給C講思路,讓B自己整理步驟。這樣老師就沒有空著,一直在做事情,很快就完成了三個(gè)任務(wù)。與順序執(zhí)行不同的是,順序執(zhí)行中老師講完思路之后學(xué)生在寫步驟,這在這期間,老師是完全空著的,沒做事的,所以效率低下。

3.并行:直接讓三個(gè)老師甲、乙、丙三個(gè)老師“同時(shí)”給三個(gè)學(xué)生輔導(dǎo)作業(yè),也完成的很快。

二、例二

1.順序執(zhí)行:你吃飯吃到一半,電話來了,你一直到吃完了以后才去接,這就說明你不支持并發(fā)也不支持并行。

2.并發(fā):并發(fā)應(yīng)該是一手筷子,一手電話,說一句話,咽一口飯。

3.并行是咽一口飯同時(shí)說一句話,而這光靠一張嘴是辦不到的,至少兩張嘴。

三、理解

1.并行是指兩個(gè)或者多個(gè)事件在同一時(shí)刻發(fā)生;而并發(fā)是指兩個(gè)或多個(gè)事件在同一時(shí)間間隔發(fā)生。

2.并行是在不同實(shí)體上的多個(gè)事件,并發(fā)是在同一實(shí)體上的多個(gè)事件。

3.在一臺(tái)處理器上“同時(shí)”處理多個(gè)任務(wù),在多臺(tái)處理器上同時(shí)處理多個(gè)任務(wù)。如hadoop分布式集群

普通解釋:
并發(fā):交替做不同事情的能力
并行:同時(shí)做不同事情的能力
專業(yè)術(shù)語:
并發(fā):不同的代碼塊交替執(zhí)行
并行:不同的代碼塊同時(shí)執(zhí)行

并發(fā)和并行的意義:
并發(fā)和并行都可以處理“多任務(wù)”,二者的主要區(qū)別在于是否是“同時(shí)進(jìn)行”多個(gè)的任務(wù)。

四、摘自:《并發(fā)的藝術(shù)》 — 〔美〕布雷謝斯

如果某個(gè)系統(tǒng)支持兩個(gè)或者多個(gè)動(dòng)作(Action)同時(shí)存在,那么這個(gè)系統(tǒng)就是一個(gè)并發(fā)系統(tǒng)。如果某個(gè)系統(tǒng)支持兩個(gè)或者多個(gè)動(dòng)作同時(shí)執(zhí)行,那么這個(gè)系統(tǒng)就是一個(gè)并行系統(tǒng)。并發(fā)系統(tǒng)與并行系統(tǒng)這兩個(gè)定義之間的關(guān)鍵差異在于“存在”這個(gè)詞。

在并發(fā)程序中可以同時(shí)擁有兩個(gè)或者多個(gè)線程。這意味著,如果程序在單核處理器上運(yùn)行,那么這兩個(gè)線程將交替地?fù)Q入或者換出內(nèi)存。這些線程是同時(shí)“存在”的——每個(gè)線程都處于執(zhí)行過程中的某個(gè)狀態(tài)。如果程序能夠并行執(zhí)行,那么就一定是運(yùn)行在多核處理器上。此時(shí),程序中的每個(gè)線程都將分配到一個(gè)獨(dú)立的處理器核上,因此可以同時(shí)運(yùn)行。

我相信你已經(jīng)能夠得出結(jié)論——“并行”概念是“并發(fā)”概念的一個(gè)子集。也就是說,你可以編寫一個(gè)擁有多個(gè)線程或者進(jìn)程的并發(fā)程序,但如果沒有多核處理器來執(zhí)行這個(gè)程序,那么就不能以并行方式來運(yùn)行代碼。因此,凡是在求解單個(gè)問題時(shí)涉及多個(gè)執(zhí)行流程的編程模式或者執(zhí)行行為,都屬于并發(fā)編程的范疇。

并發(fā)和并行都可以是很多個(gè)線程,就看這些線程能不能同時(shí)被(多個(gè))cpu執(zhí)行,如果可以就說明是并行,而并發(fā)是多個(gè)線程被(一個(gè))cpu 輪流切換著執(zhí)行。

五、摘自《go語言實(shí)戰(zhàn)》P125

并發(fā)(concurrency)不是并行(parallelism)。并行是讓不同的代碼片段同時(shí)在不同的物理處理器上執(zhí)行。并行的關(guān)鍵是同時(shí)做很多事情,而并發(fā)是指同時(shí)管理很多事情,這些事情可能只做了一半就被暫停去做別的事情了。在很多情況下,并發(fā)的效果比并行好,因?yàn)椴僮飨到y(tǒng)和硬件的總資源一般很少,但能支持系統(tǒng)同時(shí)做很多事情。這種“使用較少的資源做更多的事情” 的哲學(xué),也是指導(dǎo) Go 語言設(shè)計(jì)的哲學(xué)。

如果希望讓 goroutine 并行,必須使用多于一個(gè)邏輯處理器。 當(dāng)有多個(gè)邏輯處理器時(shí),調(diào)度器會(huì)將 goroutine 平等分配到每個(gè)邏輯處理器上。這會(huì)讓 goroutine 在不同的線程上運(yùn)行。不過要想真的實(shí)現(xiàn)并行的效果,用戶需要讓自己的程序運(yùn)行在有多個(gè)物理處理器的機(jī)器上。否則,哪怕 Go 語言運(yùn)行時(shí)使用多個(gè)線程, goroutine 依然會(huì)在同一個(gè)物理處理器上并發(fā)運(yùn)行,達(dá)不到并行的效果。

圖 6-3展示了在一個(gè)邏輯處理器上并發(fā)運(yùn)行 goroutine和在兩個(gè)邏輯處理器上并行運(yùn)行兩個(gè)并發(fā)的 goroutine 之間的區(qū)別。調(diào)度器包含一些聰明的算法,這些算法會(huì)隨著 Go語言的發(fā)布被更新和改進(jìn),所以不推薦盲目修改語言運(yùn)行時(shí)對邏輯處理器的默認(rèn)設(shè)置。如果真的認(rèn)為修改邏輯處理器的數(shù)量可以改進(jìn)性能,也可以對語言運(yùn)行時(shí)的參數(shù)進(jìn)行細(xì)微調(diào)整。后面會(huì)介紹如何做這種修改。


image.png
12 func main() {
13 // 分配一個(gè)邏輯處理器給調(diào)度器使用
14 runtime.GOMAXPROCS(1)
15
16 // wg 用來等待程序完成
17 // 計(jì)數(shù)加 2,表示要等待兩個(gè) goroutine
18 var wg sync.WaitGroup
19 wg.Add(2)
20
21 fmt.Println("Start Goroutines")
22
23 // 聲明一個(gè)匿名函數(shù),并創(chuàng)建一個(gè) goroutine
24 go func() {
25 // 在函數(shù)退出時(shí)調(diào)用 Done 來通知 main 函數(shù)工作已經(jīng)完成
26 defer wg.Done()
27
28 // 顯示字母表 3 次
29 for count := 0; count < 3; count++ {
30 for char := 'a'; char < 'a'+26; char++ {
31 fmt.Printf("%c ", char)
32 }
33 }
34 }()
35
36 // 聲明一個(gè)匿名函數(shù),并創(chuàng)建一個(gè) goroutine
37 go func() {
38 // 在函數(shù)退出時(shí)調(diào)用 Done 來通知 main 函數(shù)工作已經(jīng)完成
39 defer wg.Done()
40
41 // 顯示字母表 3 次
42 for count := 0; count < 3; count++ {
43 for char := 'A'; char < 'A'+26; char++ {
44 fmt.Printf("%c ", char)
45 }
46 }
47 }()
48
49 // 等待 goroutine 結(jié)束
50 fmt.Println("Waiting To Finish")
51 wg.Wait()
52
53 fmt.Println("\nTerminating Program")
54 }

在代碼清單 6-1 的第 14 行,調(diào)用了 runtime 包的 GOMAXPROCS 函數(shù)。這個(gè)函數(shù)允許程序更改調(diào)度器可以使用的邏輯處理器的數(shù)量。如果不想在代碼里做這個(gè)調(diào)用,也可以通過修改和這個(gè)函數(shù)名字一樣的環(huán)境變量的值來更改邏輯處理器的數(shù)量。給這個(gè)函數(shù)傳入 1,是通知調(diào)度器只能為該程序使用一個(gè)邏輯處理器。

Start Goroutines
Waiting To Finish
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z A B C D E F G H I J K L M
N O P Q R S T U V W X Y Z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m
n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z
Terminating Program

第一個(gè) goroutine 完成所有顯示需要花時(shí)間太短了,以至于在調(diào)度器切換到第二個(gè) goroutine之前,就完成了所有任務(wù)。這也是為什么會(huì)看到先輸出了所有的大寫字母,之后才輸出小寫字母。我們創(chuàng)建的兩個(gè) goroutine 一個(gè)接一個(gè)地并發(fā)運(yùn)行,獨(dú)立完成顯示字母表的任務(wù)。

代碼清單 6-6 如何修改邏輯處理器的數(shù)量

import "runtime"
// 給每個(gè)可用的核心分配一個(gè)邏輯處理器
runtime.GOMAXPROCS(runtime.NumCPU())

包 runtime 提供了修改 Go 語言運(yùn)行時(shí)配置參數(shù)的能力。在代碼清單 6-6 里,我們使用兩個(gè) runtime 包的函數(shù)來修改調(diào)度器使用的邏輯處理器的數(shù)量。函數(shù) NumCPU 返回可以使用的物理處理器的數(shù)量。因此,調(diào)用 GOMAXPROCS 函數(shù)就為每個(gè)可用的物理處理器創(chuàng)建一個(gè)邏輯處理器。需要強(qiáng)調(diào)的是,使用多個(gè)邏輯處理器并不意味著性能更好。在修改任何語言運(yùn)行時(shí)配置參數(shù)的時(shí)候,都需要配合基準(zhǔn)測試來評估程序的運(yùn)行效果。如果給調(diào)度器分配多個(gè)邏輯處理器,我們會(huì)看到之前的示例程序的輸出行為會(huì)有些不同。讓我們把邏輯處理器的數(shù)量改為 2(runtime.GOMAXPROCS(2)),并再次運(yùn)行第一個(gè)打印英文字母表的示例程序

代碼清單 6-8 listing07.go 的輸出

Create Goroutines
Waiting To Finish
A B C a D E b F c G d H e I f J g K h L i M j N k O l P m Q n R o S p T
q U r V s W t X u Y v Z w A x B y C z D a E b F c G d H e I f J g K h L
i M j N k O l P m Q n R o S p T q U r V s W t X u Y v Z w A x B y C z D
a E b F c G d H e I f J g K h L i M j N k O l P m Q n R o S p T q U r V
s W t X u Y v Z w x y z
Terminating Program

如果仔細(xì)查看代碼清單 6-8 中的輸出,會(huì)看到 goroutine 是并行運(yùn)行的。兩個(gè) goroutine 幾乎是同時(shí)開始運(yùn)行的,大小寫字母是混合在一起顯示的。這是在一臺(tái) 8 核的電腦上運(yùn)行程序的輸出,所以每個(gè) goroutine 獨(dú)自運(yùn)行在自己的核上。記住,只有在有多個(gè)邏輯處理器且可以同時(shí)讓每個(gè)goroutine 運(yùn)行在一個(gè)可用的物理處理器上的時(shí)候, goroutine 才會(huì)并行運(yùn)行。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • iOS多線程編程 基本知識(shí) 1. 進(jìn)程(process) 進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序,就是一段程序的執(zhí)...
    陵無山閱讀 6,364評論 1 14
  • 進(jìn)程和線程 進(jìn)程 所有運(yùn)行中的任務(wù)通常對應(yīng)一個(gè)進(jìn)程,當(dāng)一個(gè)程序進(jìn)入內(nèi)存運(yùn)行時(shí),即變成一個(gè)進(jìn)程.進(jìn)程是處于運(yùn)行過程中...
    勝浩_ae28閱讀 5,261評論 0 23
  • 必備的理論基礎(chǔ) 1.操作系統(tǒng)作用: 隱藏丑陋復(fù)雜的硬件接口,提供良好的抽象接口。 管理調(diào)度進(jìn)程,并將多個(gè)進(jìn)程對硬件...
    drfung閱讀 3,778評論 0 5
  • 作者: 一字馬胡 轉(zhuǎn)載標(biāo)志 【2017-11-01】 更新日志 日期更新內(nèi)容備注2017-11-01新建文章V1...
    一字馬胡閱讀 7,618評論 9 134
  • 在幾十年前,我們那些前輩們上大學(xué)時(shí),學(xué)生刻苦努力學(xué)習(xí)新知識(shí),求知若渴,虛懷若谷,一顆赤子之心無疑。老師也是...
    深塵閱讀 507評論 0 0

友情鏈接更多精彩內(nèi)容