并發(fā)的概念及其重要性
這段是簡(jiǎn)單科普沾鳄,大佬可以跳過(guò)
并發(fā):并發(fā)程序指同時(shí)進(jìn)行多個(gè)任務(wù)的程序。在操作系統(tǒng)中确憨,是指一個(gè)時(shí)間段中有幾個(gè)程序都處于已啟動(dòng)運(yùn)行到運(yùn)行完畢之間译荞,且這幾個(gè)程序都是在同一個(gè)處理機(jī)上運(yùn)行,但任一個(gè)時(shí)刻點(diǎn)上只有一個(gè)程序在處理機(jī)上運(yùn)行休弃。
----------本段引用內(nèi)容源自《GO語(yǔ)言高級(jí)編程》
在早期吞歼,CPU都是以單核的形式順序執(zhí)行機(jī)器指令。Go語(yǔ)言的祖先C語(yǔ)言正是這種順序編程語(yǔ)言的代表塔猾。順序編程語(yǔ)言中的順序是指:所有的指令都是以串行的方式執(zhí)行浆熔,在相同的時(shí)刻有且僅有一個(gè)CPU在順序執(zhí)行程序的指令。
隨著處理器技術(shù)的發(fā)展,單核時(shí)代以提升處理器頻率來(lái)提高運(yùn)行效率的方式遇到了瓶頸医增,目前各種主流的CPU頻率基本被鎖定在了3GHZ附近慎皱。單核CPU的發(fā)展的停滯,給多核CPU的發(fā)展帶來(lái)了機(jī)遇叶骨。相應(yīng)地茫多,編程語(yǔ)言也開(kāi)始逐步向并行化的方向發(fā)展。Go語(yǔ)言正是在多核和網(wǎng)絡(luò)化的時(shí)代背景下誕生的原生支持并發(fā)的編程語(yǔ)言忽刽。
在聊并發(fā)之前天揖,聊聊共享變量、線(xiàn)程跪帝、協(xié)程
- 如何在不同線(xiàn)程/協(xié)程 共享 變量/內(nèi)存今膊?
這里留給各位看官去自行查資料,即使我列出來(lái)也不如自己動(dòng)手去查記憶深刻伞剑!
不想查也可以等我下一篇文章斑唬,更加詳細(xì)解讀線(xiàn)程、進(jìn)程黎泣。
- 線(xiàn)程和協(xié)程概念恕刘?
線(xiàn)程:線(xiàn)程是操作系統(tǒng)能夠進(jìn)行運(yùn)算調(diào)度的最小單位。一個(gè)進(jìn)程可以包含多個(gè)線(xiàn)程抒倚,是進(jìn)程中的實(shí)際運(yùn)作單位褐着。
協(xié)程:又稱(chēng)微線(xiàn)程。協(xié)程是一種用戶(hù)態(tài)的輕量級(jí)線(xiàn)程托呕。協(xié)程擁有自己的寄存器上下文和棧含蓉。協(xié)程調(diào)度切換時(shí),將寄存器上下文和棧保存到其他地方项郊,在切回來(lái)的時(shí)候谴餐,恢復(fù)先前保存的寄存器上下文和棧。
- 為什么會(huì)誕生協(xié)程呆抑?
雖然多線(xiàn)程在前互聯(lián)網(wǎng)世代已經(jīng)足夠使用岂嗓,但是線(xiàn)程的局限性也比較明顯
- 線(xiàn)程數(shù)量有限,一般不會(huì)很多
- 線(xiàn)程占據(jù)的資源通常比我們需要的多得多鹊碍,造成浪費(fèi)
每個(gè)系統(tǒng)級(jí)線(xiàn)程開(kāi)辟都會(huì)占用空間厌殉,這個(gè)空間可能是MB級(jí)別,但是我們?nèi)绻褂玫木€(xiàn)程只需要傳遞KB級(jí)別數(shù)據(jù)侈咕,那么線(xiàn)程看起來(lái)就會(huì)比較浪費(fèi)公罕,但是又不可避免。而且線(xiàn)程之間的切換也會(huì)占用一些額外開(kāi)銷(xiāo)耀销。
為了解決上面的矛盾問(wèn)題楼眷,協(xié)程誕生了:更小的資源開(kāi)支,動(dòng)態(tài)調(diào)配資源,比線(xiàn)程更輕量罐柳。
協(xié)程的一些優(yōu)點(diǎn):
- 因?yàn)樽映绦蚯袚Q不是線(xiàn)程切換掌腰,而是由程序自身控制,因此张吉,沒(méi)有線(xiàn)程切換的開(kāi)銷(xiāo)齿梁,和多線(xiàn)程比,線(xiàn)程數(shù)量越多肮蛹,協(xié)程的性能優(yōu)勢(shì)就越明顯勺择。
- 不需要多線(xiàn)程的鎖機(jī)制,因?yàn)橹挥幸粋€(gè)線(xiàn)程伦忠,也不存在同時(shí)寫(xiě)變量沖突省核,在協(xié)程中控制共享資源不加鎖,只需要判斷狀態(tài)就好了昆码,所以執(zhí)行效率比多線(xiàn)程高很多气忠。
在golang中,goroutine的創(chuàng)建消耗非常小未桥,大約是KB級(jí)別笔刹。因此可以創(chuàng)建更多的協(xié)程芥备,尤其是數(shù)量越多相對(duì)線(xiàn)程優(yōu)勢(shì)更加明顯冬耿,而且goroutine可以動(dòng)態(tài)伸縮,棧溢出風(fēng)險(xiǎn)也比線(xiàn)程更低萌壳。
golang的并發(fā)亦镶,goroutine的使用
var name = "yiyiyinhe"
func changeName() {
name = "change"
}
func sayHi() {
fmt.println("hi, ", name)
go changeName() // 協(xié)程
}
簡(jiǎn)單的協(xié)程就創(chuàng)建了,那么打印出來(lái)的結(jié)果可能是hi, yiyiyinhe
也可能是hi, change
袱瓮。
如果想對(duì)某一代碼塊執(zhí)行協(xié)程而不是某個(gè)方法缤骨,則使用下面方式
var name = "yiyiyinhe"
func sayHi() {
fmt.println("hi, ", name)
go func() { // 匿名函數(shù)執(zhí)行協(xié)程
name = "change"
}
}
channel
golang對(duì)共享變量的口號(hào)
Do not communicate by sharing memory; instead, share memory by communicating.
不要通過(guò)共享內(nèi)存來(lái)通信,而應(yīng)通過(guò)通信來(lái)共享內(nèi)存尺借。
那么在協(xié)程中也需要進(jìn)行通信绊起,而golang使用的goroutine之間通信和同步的主要方法是channel。
什么是channel呢燎斩?
A channel is a communic ation mechanism that lets one goroutine send values to another goroutine. Each channel is a conduit for values of a particular type, called the channel’s element type.
channel是一種通信機(jī)制虱歪,它讓一個(gè)goroutine向另一個(gè)goroutine發(fā)送值。每個(gè)通道都是特定類(lèi)型(通道元素類(lèi)型)值的管道栅表。
簡(jiǎn)單來(lái)理解就是笋鄙,channel是用于在goroutine中進(jìn)行通信的管道,而且管道是有特定類(lèi)型的怪瓶。
創(chuàng)建的channel分為有緩存和無(wú)緩存兩種萧落,區(qū)別就是創(chuàng)建的時(shí)候是否分配大小
-
無(wú)緩存channel
-
var ch1 = make(chan int)
,未分配大小 -
var ch2 = make(chan int, 0)
,分配大小為0也等同給于未分配大小
-
-
有緩存channel
-
var :ch3 = make(int, 3)
找岖,分配大小為3的有緩存channel
-
在無(wú)緩存channel中陨倡,channel的發(fā)送操作總是在接收之前發(fā)生;簡(jiǎn)單理解就是宣增,無(wú)緩存channel是一個(gè)管道必須從頭flag<-true
發(fā)送到尾部<-flag
玫膀,而且尾部發(fā)生的時(shí)間一定是在頭部發(fā)送之后。
-
為什么chennel可以這樣呢爹脾?
因?yàn)閏hannel有阻塞作用帖旨,必須接收了才能繼續(xù)下去。
有緩存channel則不具備上述的特性灵妨,因?yàn)閷?duì)于帶緩沖的Channel解阅,對(duì)于Channel的第 K 個(gè)接收完成操作發(fā)生在第 K+C 個(gè)發(fā)送操作完成之前,其中 C 是Channel的緩存大小泌霍。 如果將 C 設(shè)置為0自然就對(duì)應(yīng)無(wú)緩存的Channel货抄,也即使第K個(gè)接收完成在第K個(gè)發(fā)送完成之前。因?yàn)闊o(wú)緩存的Channel只能同步發(fā)1個(gè)朱转,也就簡(jiǎn)化為前面無(wú)緩存Channel的規(guī)則:對(duì)于從無(wú)緩沖Channel進(jìn)行的接收蟹地,發(fā)生在對(duì)該Channel進(jìn)行的發(fā)送完成之前。
還是上面的例子藤为,使用channel來(lái)演示一下
var name = "yiyi"
var flag = make(chan bool) // 創(chuàng)建了bool類(lèi)型的channel
func changeName() {
name = "change"
flag <- true // 發(fā)送
}
func sayHi() {
go changeName() // 協(xié)程
<-flag // 接收
fmt.println("hi, ", name)
}
那么這個(gè)時(shí)候打印出來(lái)的就是一個(gè)固定的順序怪与,由于<-flag
接收總是在發(fā)送
之后執(zhí)行,因此當(dāng)flag <- true
執(zhí)行完之前name = "change"
已經(jīng)執(zhí)行缅疟,打印結(jié)果一定是:hi, change
上面代碼等同于下圖所示
看完你可以收獲什么分别?
- 簡(jiǎn)單了解并發(fā),了解多線(xiàn)程簡(jiǎn)單的發(fā)展來(lái)歷
- 簡(jiǎn)單了解線(xiàn)程存淫,協(xié)程
- 為什么協(xié)程會(huì)誕生耘斩?
- goroutine的兩種使用方式
- channel是什么?channel的兩種分類(lèi)桅咆;
- channel在goroutine中有什么作用括授?
寫(xiě)在最后
由于我剛開(kāi)始寫(xiě)技術(shù)文章,很多東西不知道怎么寫(xiě)才能讓大家都看懂岩饼,就像寫(xiě)線(xiàn)程協(xié)程的時(shí)候不知道要不要解釋共享變量或者共享內(nèi)存是什么荚虚,也不知道大家能不能知道多線(xiàn)程模式下線(xiàn)程之間通信有哪些方式,感覺(jué)都想寫(xiě)但是又覺(jué)得大家看文章標(biāo)題應(yīng)該是了解一些東西的忌愚,如果都寫(xiě)篇幅太長(zhǎng)曲管,不寫(xiě)又看不懂;就覺(jué)得比較矛盾吧硕糊,也希望大家能夠給我提一些意見(jiàn)建議院水!
作者還在慢慢努力腊徙,盡量把文章寫(xiě)的通俗易懂,排版準(zhǔn)確檬某,抓住重點(diǎn)撬腾,把最好的內(nèi)容展現(xiàn)給大家。