Go 并發(fā)
Go?語(yǔ)言支持并發(fā),我們只需要通過(guò)?go?關(guān)鍵字來(lái)開(kāi)啟?goroutine?即可。
goroutine?是輕量級(jí)線程,goroutine?的調(diào)度是由?Golang?運(yùn)行時(shí)進(jìn)行管理的。
goroutine?語(yǔ)法格式:
go?函數(shù)名(?參數(shù)列表?)
例如:
go?f(x,?y,?z)
開(kāi)啟一個(gè)新的?goroutine:
f(x,?y,?z)
Go?允許使用?go?語(yǔ)句開(kāi)啟一個(gè)新的運(yùn)行期線程,?即?goroutine,以一個(gè)不同的、新創(chuàng)建的?goroutine?來(lái)執(zhí)行一個(gè)函數(shù)。?同一個(gè)程序中的所有?goroutine?共享同一個(gè)地址空間。
實(shí)例
package?main
import?(
"fmt"
"time"
)
func?say(s?string)?{
for?i?:=?0;?i?<?5;?i++?{
time.Sleep(100?*?time.Millisecond)
fmt.Println(s)
}
}
func?main()?{
go?say("world")
say("hello")
}
執(zhí)行以上代碼,你會(huì)看到輸出的?hello?和?world?是沒(méi)有固定先后順序。因?yàn)樗鼈兪莾蓚€(gè)?goroutine?在執(zhí)行:
worldhellohelloworldworldhellohelloworldworldhello
通道(channel)
通道(channel)是用來(lái)傳遞數(shù)據(jù)的一個(gè)數(shù)據(jù)結(jié)構(gòu)。
通道可用于兩個(gè)?goroutine?之間通過(guò)傳遞一個(gè)指定類(lèi)型的值來(lái)同步運(yùn)行和通訊。操作符?<-?用于指定通道的方向,發(fā)送或接收。如果未指定方向,則為雙向通道。
ch?<-?v????//?把?v?發(fā)送到通道?chv?:=?<-ch??//?從?ch?接收數(shù)據(jù)//?并把值賦給?v
聲明一個(gè)通道很簡(jiǎn)單,我們使用chan關(guān)鍵字即可,通道在使用前必須先創(chuàng)建:
ch?:=?make(chanint)
注意:默認(rèn)情況下,通道是不帶緩沖區(qū)的。發(fā)送端發(fā)送數(shù)據(jù),同時(shí)必須有接收端相應(yīng)的接收數(shù)據(jù)。
以下實(shí)例通過(guò)兩個(gè)?goroutine?來(lái)計(jì)算數(shù)字之和,在?goroutine?完成計(jì)算后,它會(huì)計(jì)算兩個(gè)結(jié)果的和:
實(shí)例
package?main
import?"fmt"
func?sum(s?[]int,?c?chan?int)?{
sum?:=?0
for?_,?v?:=?range?s?{
sum?+=?v
}
c?<-?sum?//?把?sum?發(fā)送到通道?c
}
func?main()?{
s?:=?[]int{7,?2,?8,?-9,?4,?0}
c?:=?make(chan?int)
go?sum(s[:len(s)/2],?c)
go?sum(s[len(s)/2:],?c)
x,?y?:=?<-c,?<-c?//?從通道?c?中接收
fmt.Println(x,?y,?x+y)
}
輸出結(jié)果為:
-5?17?12
通道緩沖區(qū)
通道可以設(shè)置緩沖區(qū),通過(guò)?make?的第二個(gè)參數(shù)指定緩沖區(qū)大小:
ch?:=?make(chanint,?100)
帶緩沖區(qū)的通道允許發(fā)送端的數(shù)據(jù)發(fā)送和接收端的數(shù)據(jù)獲取處于異步狀態(tài),就是說(shuō)發(fā)送端發(fā)送的數(shù)據(jù)可以放在緩沖區(qū)里面,可以等待接收端去獲取數(shù)據(jù),而不是立刻需要接收端去獲取數(shù)據(jù)。
不過(guò)由于緩沖區(qū)的大小是有限的,所以還是必須有接收端來(lái)接收數(shù)據(jù)的,否則緩沖區(qū)一滿,數(shù)據(jù)發(fā)送端就無(wú)法再發(fā)送數(shù)據(jù)了。
注意:如果通道不帶緩沖,發(fā)送方會(huì)阻塞直到接收方從通道中接收了值。如果通道帶緩沖,發(fā)送方則會(huì)阻塞直到發(fā)送的值被拷貝到緩沖區(qū)內(nèi);如果緩沖區(qū)已滿,則意味著需要等待直到某個(gè)接收方獲取到一個(gè)值。接收方在有值可以接收之前會(huì)一直阻塞。
實(shí)例
package?main
import?"fmt"
func?main()?{
//?這里我們定義了一個(gè)可以存儲(chǔ)整數(shù)類(lèi)型的帶緩沖通道
//?緩沖區(qū)大小為2
ch?:=?make(chan?int,?2)
//?因?yàn)?ch?是帶緩沖的通道,我們可以同時(shí)發(fā)送兩個(gè)數(shù)據(jù)
//?而不用立刻需要去同步讀取數(shù)據(jù)
ch?<-?1
ch?<-?2
//?獲取這兩個(gè)數(shù)據(jù)
fmt.Println(<-ch)
fmt.Println(<-ch)
}
執(zhí)行輸出結(jié)果為:
12
Go?遍歷通道與關(guān)閉通道
Go?通過(guò)?range?關(guān)鍵字來(lái)實(shí)現(xiàn)遍歷讀取到的數(shù)據(jù),類(lèi)似于與數(shù)組或切片。格式如下:
v,?ok?:=?<-ch
如果通道接收不到數(shù)據(jù)后?ok?就為?false,這時(shí)通道就可以使用?close()?函數(shù)來(lái)關(guān)閉。
實(shí)例
package?main
import?(
"fmt"
)
func?fibonacci(n?int,?c?chan?int)?{
x,?y?:=?0,?1
for?i?:=?0;?i?<?n;?i++?{
c?<-?x
x,?y?=?y,?x+y
}
close(c)
}
func?main()?{
c?:=?make(chan?int,?10)
go?fibonacci(cap(c),?c)
//?range?函數(shù)遍歷每個(gè)從通道接收到的數(shù)據(jù),因?yàn)?c?在發(fā)送完?10?個(gè)
//?數(shù)據(jù)之后就關(guān)閉了通道,所以這里我們?range?函數(shù)在接收到?10?個(gè)數(shù)據(jù)
//?之后就結(jié)束了。如果上面的?c?通道不關(guān)閉,那么?range?函數(shù)就不
//?會(huì)結(jié)束,從而在接收第?11?個(gè)數(shù)據(jù)的時(shí)候就阻塞了。
for?i?:=?range?c?{
fmt.Println(i)
}
}
執(zhí)行輸出結(jié)果為:
0112358132134