一户誓、并發(fā)與并行
1. 并發(fā)
并發(fā)(Concurrent):1個(gè)CPU交錯(cuò)執(zhí)行2個(gè)任務(wù)袁串。單核系統(tǒng)中者冤,進(jìn)程(或線程)通過(guò)時(shí)間片或出讓控制權(quán)來(lái)實(shí)現(xiàn)任務(wù)切換无宿,以達(dá)到“同時(shí)”運(yùn)行多個(gè)程序的目的,實(shí)際上任何時(shí)刻都只有1個(gè)任務(wù)被執(zhí)行赂弓。宏觀上是“同時(shí)”執(zhí)行绑榴,微觀上是交錯(cuò)地順序執(zhí)行。
并發(fā)的特性
- 系統(tǒng)資源被多個(gè)進(jìn)程(或線程)共享盈魁,造成程序結(jié)果不唯一
- 進(jìn)程(或線程)結(jié)果的多變翔怎,導(dǎo)致進(jìn)程(或線程)運(yùn)行會(huì)出現(xiàn)不同的結(jié)果或偶發(fā)的異常
- 多個(gè)進(jìn)程(或線程)間存在競(jìng)爭(zhēng)資源產(chǎn)生的互斥關(guān)系,也存在協(xié)作完成一個(gè)整體任務(wù)產(chǎn)生的同步關(guān)系杨耙。
能否很好地解決多個(gè)進(jìn)程(或線程)間的同步及互斥關(guān)系赤套,將決定程序能否正常運(yùn)行。
2. 并行
并行(Parallel):2個(gè)CPU分別同時(shí)各執(zhí)行了1個(gè)任務(wù)珊膜。多核系統(tǒng)中容握,理想情況下,可以讓多個(gè)進(jìn)程(或線程)做到真正意義上的同時(shí)執(zhí)行车柠,它們之間不需要排隊(duì)
通過(guò)下圖中Erlang 之父 Joe Armstrong對(duì)并發(fā)與并行的說(shuō)明剔氏,我們能清晰的區(qū)分并發(fā)與并行
二、進(jìn)程竹祷、線程谈跛、協(xié)程
1. 進(jìn)程
進(jìn)程(Process):是操作系統(tǒng)進(jìn)行資源分配和調(diào)度的一個(gè)獨(dú)立單位。是一個(gè)具有特定功能的程序運(yùn)行在一個(gè)數(shù)據(jù)集上的一次動(dòng)態(tài)過(guò)程塑陵。是應(yīng)用程序運(yùn)行的載體感憾。操作系統(tǒng)內(nèi)核通過(guò)進(jìn)程控制塊(PCB,process control block)來(lái)感知進(jìn)程令花。
進(jìn)程的組成
- 程序:用于描述進(jìn)程要完成的功能阻桅,是控制進(jìn)程執(zhí)行的指令集凉倚。(進(jìn)程的執(zhí)行)
- 數(shù)據(jù)集合:程序執(zhí)行時(shí)所需的數(shù)據(jù)和工作空間。(進(jìn)程的數(shù)據(jù)資源)
- 進(jìn)程控制塊(PCB):但基本包括進(jìn)程標(biāo)識(shí)符嫂沉,當(dāng)前狀態(tài)占遥,現(xiàn)場(chǎng)保護(hù)區(qū),存儲(chǔ)指針输瓜,占用資源表以及進(jìn)程優(yōu)先級(jí)等信息。它是進(jìn)程存在的唯一標(biāo)志芬萍。(進(jìn)程的詳細(xì)信息)
進(jìn)程的切換
進(jìn)程切換尤揣,就是把進(jìn)程存放在處理器的寄存器中的中間數(shù)據(jù)存放到進(jìn)程的私有堆棧中,從而把處理器的寄存器騰出來(lái)讓其他進(jìn)程使用柬祠。這個(gè)中間數(shù)據(jù)北戏,就被稱作該進(jìn)程的上下文。進(jìn)程的切換實(shí)質(zhì)上就是被中止運(yùn)行進(jìn)程與待運(yùn)行進(jìn)程上下文的切換
進(jìn)程的狀態(tài)
就緒狀態(tài):進(jìn)程已獲得除處理器外的所需資源漫蛔,等待分配處理器資源嗜愈;只要分配了處理器進(jìn)程就可執(zhí)行。就緒進(jìn)程可以按多個(gè)優(yōu)先級(jí)來(lái)劃分隊(duì)列莽龟。
運(yùn)行狀態(tài):占有CPU蠕嫁,在CPU上執(zhí)行。
阻塞狀態(tài):由于進(jìn)程等待某種條件(如I/O操作或進(jìn)程同步)毯盈,在條件滿足之前無(wú)法繼續(xù)執(zhí)行剃毒。
創(chuàng)建狀態(tài):進(jìn)程正在被創(chuàng)建,系統(tǒng)為其初始化PCB搂赋,分配資源赘阀。
終止?fàn)顟B(tài):進(jìn)程正在從系統(tǒng)中撤銷,回收進(jìn)程的資源脑奠,撤銷其PCB基公。
進(jìn)程的通信方式
-
管道
管道可以看成是一種只存在內(nèi)存中,不存在于任何文件系統(tǒng)中的特殊文件宋欺,支持普通的read轰豆、write 等函數(shù)。分為以下兩種管道
匿名管道(pipe):
半雙工(即數(shù)據(jù)只能在一個(gè)方向上流動(dòng))齿诞,具有固定的讀端和寫端秒咨,只能用于具有親緣關(guān)系的進(jìn)程之間的通信。
命名管道(FIFO):
半雙工掌挚,有自己的名字和訪問(wèn)權(quán)限的限制雨席,就像一個(gè)文件一樣,它可以用于不相關(guān)進(jìn)程間的通信吠式,進(jìn)程通過(guò)使用命名管道的名字獲得管道陡厘。管道的特點(diǎn):寫滿時(shí)抽米,不能再寫;讀空時(shí)糙置,不能再讀
使用場(chǎng)景:適合兩個(gè)進(jìn)程間發(fā)送非常短小的云茸、頻率很高的消息。 消息隊(duì)列
由消息組成的鏈表谤饭,存放在內(nèi)核中并由消息隊(duì)列標(biāo)識(shí)符標(biāo)識(shí)标捺。消息隊(duì)列是消息的鏈接表,包括Posix消息隊(duì)列system V消息隊(duì)列揉抵。有足夠權(quán)限的進(jìn)程可以向隊(duì)列中添加消息亡容,被賦予讀權(quán)限的進(jìn)程則可以讀走隊(duì)列中的消息。消息隊(duì)列克服了信號(hào)承載信息量少冤今,管道只能承載無(wú)格式字節(jié)流以及緩沖區(qū)大小受限等缺點(diǎn)闺兢。信號(hào)
信號(hào)是一種非常短的消息,短到只有一個(gè)數(shù)字戏罢。一個(gè)進(jìn)程可以向另外一個(gè)進(jìn)程或者另外一組進(jìn)程發(fā)送信號(hào)消息屋谭,通知目標(biāo)進(jìn)程執(zhí)行特定的代碼信號(hào)量
用于實(shí)現(xiàn)進(jìn)程間的互斥與同步,而不用于存儲(chǔ)進(jìn)程間通信數(shù)據(jù)龟糕,是一種保證共享資源有序訪問(wèn)的工具桐磁。共享內(nèi)存
允許兩個(gè)或多個(gè)進(jìn)程共享一個(gè)給定的存儲(chǔ)區(qū),這一段存儲(chǔ)區(qū)可以被兩個(gè)或兩個(gè)以上的進(jìn)程映射至自身的地址空間中讲岁,一個(gè)進(jìn)程寫入共享內(nèi)存的信息所意,可以被其他使用這個(gè)共享內(nèi)存的進(jìn)程。
共享內(nèi)存將保持到通信完畢為止催首,不會(huì)頻繁解除內(nèi)存映射或重建內(nèi)存共享區(qū)域扶踊,因此共享內(nèi)存的通信方式效率非常高。
使用場(chǎng)景:適合多進(jìn)程間共享的郎任、非常龐大的秧耗、讀寫操作頻率很高的數(shù)據(jù)通信。網(wǎng)絡(luò)Socket
網(wǎng)絡(luò)環(huán)境中進(jìn)程間通信的API舶治。與其他通信機(jī)制不同的是分井,它可用于不同機(jī)器間的進(jìn)程通信。
使用場(chǎng)景:適用于分布式開發(fā)
2. 線程
線程(thread):是操作系統(tǒng)能夠進(jìn)行運(yùn)算調(diào)度的最小單位霉猛,它被包含在進(jìn)程之中尺锚,是進(jìn)程中的實(shí)際運(yùn)作單位。
線程的分類
線程的實(shí)現(xiàn)可以分為兩類:
1)用戶級(jí)線程(User-Level Thread)
由進(jìn)程負(fù)責(zé)調(diào)度管理惜浅,不依賴于操作系統(tǒng)內(nèi)核
優(yōu)點(diǎn):
- 線程位于用戶空間(即不需要模式切換)瘫辩。
- 完全控制線程調(diào)度器(例如:網(wǎng)站服務(wù)器)。
- 獨(dú)立于操作系統(tǒng)(線程可以在不支持它們的操作系統(tǒng)上運(yùn)行)。
- 運(yùn)行時(shí)系統(tǒng)(run time system)可以切換用戶空間中的本地阻塞線程(例如:等待另一個(gè)線程完成)伐厌。
缺點(diǎn):
- 系統(tǒng)調(diào)度中承绸,對(duì)一個(gè)線程的阻塞將會(huì)導(dǎo)致整個(gè)進(jìn)程阻塞(例如:當(dāng)一個(gè)線程因 I/O 而處于等待狀態(tài)時(shí),整個(gè)進(jìn)程就會(huì)被調(diào)度程序切換為等待狀態(tài)挣轨,其他線程得不到運(yùn)行的機(jī)會(huì))军熏。
- 網(wǎng)站服務(wù)器中,一個(gè)頁(yè)面的錯(cuò)誤將導(dǎo)致整個(gè)進(jìn)程阻塞卷扮。
- 非真正意義的線程并行(一個(gè)進(jìn)程安排在單個(gè)CPU上)荡澎。
- 不存在時(shí)鐘中斷(例如,如果用戶線程是非搶占式的晤锹,將無(wú)法被“進(jìn)程調(diào)度”以round-robin的調(diào)度算法調(diào)用摩幔,因?yàn)閞ound-robin調(diào)度算法中限制了cpu時(shí)間片)。
2)內(nèi)核級(jí)線程(Kernel-Level Thread)
由操作系統(tǒng)支持和管理
優(yōu)點(diǎn):
- 實(shí)現(xiàn)了真正意義上的線程并行抖甘。
- 不需要運(yùn)行時(shí)系統(tǒng)的參與。
缺點(diǎn):
頻繁的模式切換導(dǎo)致內(nèi)核開支葫慎。
線程的同步
當(dāng)多個(gè)線程同時(shí)讀寫同一份共享資源的時(shí)候衔彻,可能會(huì)引起沖突,這時(shí)候偷办,我們需要引入線程“同步”機(jī)制艰额。線程同步是為了防止多個(gè)線程同時(shí)訪問(wèn)同一個(gè)數(shù)據(jù)對(duì)象時(shí),對(duì)數(shù)據(jù)造成破壞椒涯。線程的同步是保證多線程安全訪問(wèn)資源的一種手段柄沮。
主要通過(guò)臨界區(qū)(Critical Section)、互斥對(duì)象(Mutex)的機(jī)制來(lái)實(shí)現(xiàn)互斥控制废岂,在Java中分別對(duì)應(yīng)synchornized及對(duì)象鎖祖搓;通過(guò)信號(hào)量(Semaphore)、事件對(duì)象(Event)以通知的方式進(jìn)行同步控制湖苞,在Java中分別對(duì)應(yīng)wait()拯欧、notify()等方法。
也可以通過(guò)寫時(shí)復(fù)制(Copy On Write)的無(wú)鎖方式來(lái)實(shí)現(xiàn)線程的同步财骨,即在每個(gè)線程中拷貝一份共享資源的副本镐作。
線程的出現(xiàn),是為了分離進(jìn)程的兩個(gè)功能:資源分配和系統(tǒng)調(diào)度隆箩。讓更細(xì)粒度该贾、更輕量的線程來(lái)承擔(dān)調(diào)度,減輕調(diào)度帶來(lái)的開銷捌臊。但線程還是不夠輕量杨蛋,因?yàn)檎{(diào)度是在內(nèi)核空間進(jìn)行的,每次線程切換都需要陷入內(nèi)核,這個(gè)開銷還是不可忽視的六荒。協(xié)程則是把調(diào)度邏輯在用戶空間里實(shí)現(xiàn)护姆,通過(guò)自己(編譯器運(yùn)行時(shí)系統(tǒng)/程序員)模擬控制權(quán)的交接,來(lái)達(dá)到更加細(xì)粒度的控制掏击。
在操作系統(tǒng)的OS Thread和編程語(yǔ)言的User Thread之間卵皂,實(shí)際上存在3種線程對(duì)應(yīng)模型,也就是:1:1砚亭,1:N灯变,M:N。
- 1:1:一個(gè)用戶線程就只在一個(gè)內(nèi)核線程上跑捅膘,這時(shí)可以利用多核添祸,但是上下文切換很慢,切換效率很低寻仗。
- N:1:多個(gè)(N)用戶線程始終在一個(gè)內(nèi)核線程上跑刃泌,context上下文切換很快,但是無(wú)法真正的利用多核署尤。
- M:N:多個(gè)用戶線程在多個(gè)內(nèi)核線程上跑耙替,這個(gè)可以集齊上面兩者的優(yōu)勢(shì),既能快速切換上下文,也能利用多核的優(yōu)勢(shì)
3. 協(xié)程
協(xié)程(Coroutine):是一種用戶級(jí)的輕量線程曹体,擁有自己獨(dú)立的棧和共享的堆俗扇,共享堆,不共享?xiàng);稹f(xié)程由程序員在協(xié)程的代碼里顯示調(diào)度铜幽。進(jìn)程、線程是操作系統(tǒng)級(jí)別的概念串稀,而協(xié)程是編譯器級(jí)別的除抛,協(xié)程間切換只需要保存任務(wù)的上下文,沒(méi)有內(nèi)核的開銷母截。
協(xié)程的優(yōu)勢(shì):
- 內(nèi)存占用少
- 上下文切換代價(jià)小
4. goroutine
Goroutine基本概念:
- goroutine是Go語(yǔ)言運(yùn)行庫(kù)的功能镶殷,不是操作系統(tǒng)提供的功能,goroutine不是用線程實(shí)現(xiàn)的微酬,而是go語(yǔ)言實(shí)現(xiàn)的用戶態(tài)線程绘趋。
- goroutine就是一段代碼,一個(gè)函數(shù)入口颗管,以及在堆上為其分配的一個(gè)堆棧陷遮。所以它非常廉價(jià),我們可以很輕松的創(chuàng)建上萬(wàn)個(gè)goroutine垦江,但它們并不是被操作系統(tǒng)所調(diào)度執(zhí)行帽馋。
- goroutine來(lái)自協(xié)程的概念,讓一組可復(fù)用的函數(shù)運(yùn)行在一組線程之上,即使有g(shù)oroutine阻塞绽族,該線程的其他goroutine也可以被runtime調(diào)度姨涡,轉(zhuǎn)移到其他可運(yùn)行的線程上。這更像是多線程和協(xié)程的綜合體吧慢,能最大限度提升執(zhí)行效率涛漂,發(fā)揮多核處理能力。
Goroutine特點(diǎn):
- 占用內(nèi)存更屑焓(幾kb)
- 調(diào)度更靈活(runtime調(diào)度)
參考文檔:
https://zhuanlan.zhihu.com/p/137339439
https://zhuanlan.zhihu.com/p/260830550
https://zhuanlan.zhihu.com/p/51194025
https://www.cnblogs.com/LUO77/p/5816326.html
https://blog.csdn.net/cafucwxy/article/details/78453430
https://blog.csdn.net/zhaohong_bo/article/details/89552188
https://www.coder55.com/article/11579
http://www.sizeofvoid.net/goroutine-under-the-hood/
https://zhuanlan.zhihu.com/p/68299348