golang中并发sync和channel
golang中并發(fā)sync和channel
chenbaoke · 2014-12-08 13:00:01 · 19151 次點(diǎn)擊 · 預(yù)計(jì)閱讀時(shí)間 5 分鐘 · 不到1分鐘之前 開(kāi)始瀏覽 ? ? 這是一個(gè)創(chuàng)建于 2014-12-08 13:00:01 的文章,其中的信息可能已經(jīng)有所發(fā)展或是發(fā)生改變。golang中實(shí)現(xiàn)并發(fā)非常簡(jiǎn)單,只需在需要并發(fā)的函數(shù)前面添加關(guān)鍵字"go",但是如何處理go并發(fā)機(jī)制中不同goroutine之間的同步與通信,golang 中提供了sync包和channel機(jī)制來(lái)解決這一問(wèn)題.
sync 包提供了互斥鎖這類(lèi)的基本的同步原語(yǔ).除 Once 和 WaitGroup 之外的類(lèi)型大多用于底層庫(kù)的例程。更高級(jí)的同步操作通過(guò)信道與通信進(jìn)行。
而golang中的同步是通過(guò)sync.WaitGroup來(lái)實(shí)現(xiàn)的.WaitGroup的功能:它實(shí)現(xiàn)了一個(gè)類(lèi)似隊(duì)列的結(jié)構(gòu),可以一直向隊(duì)列中添加任務(wù),當(dāng)任務(wù)完成后便從隊(duì)列中刪除,如果隊(duì)列中的任務(wù)沒(méi)有完全完成,可以通過(guò)Wait()函數(shù)來(lái)出發(fā)阻塞,防止程序繼續(xù)進(jìn)行,直到所有的隊(duì)列任務(wù)都完成為止.
WaitGroup總共有三個(gè)方法:Add(delta int), Done(), Wait()。
Add:添加或者減少等待goroutine的數(shù)量
Done:相當(dāng)于Add(-1)
Wait:執(zhí)行阻塞,直到所有的WaitGroup數(shù)量變成0
具體例子如下:
package mainimport ("fmt""sync" )var waitgroup sync.WaitGroupfunc Afunction(shownum int) {fmt.Println(shownum)waitgroup.Done() //任務(wù)完成,將任務(wù)隊(duì)列中的任務(wù)數(shù)量-1,其實(shí).Done就是.Add(-1) }func main() {for i := 0; i < 10; i++ {waitgroup.Add(1) //每創(chuàng)建一個(gè)goroutine,就把任務(wù)隊(duì)列中任務(wù)的數(shù)量+1go Afunction(i)}waitgroup.Wait() //.Wait()這里會(huì)發(fā)生阻塞,直到隊(duì)列中所有的任務(wù)結(jié)束就會(huì)解除阻塞 }使用場(chǎng)景:程序中需要并發(fā),需要?jiǎng)?chuàng)建多個(gè)goroutine,并且一定要等這些并發(fā)全部完成后才繼續(xù)接下來(lái)的程序執(zhí)行.WaitGroup的特點(diǎn)是Wait()可以用來(lái)阻塞直到隊(duì)列中的所有任務(wù)都完成時(shí)才解除阻塞,而不需要sleep一個(gè)固定的時(shí)間來(lái)等待.但是其缺點(diǎn)是無(wú)法指定固定的goroutine數(shù)目.
Channel機(jī)制:
相對(duì)sync.WaitGroup而言,golang中利用channel實(shí)習(xí)同步則簡(jiǎn)單的多.channel自身可以實(shí)現(xiàn)阻塞,其通過(guò)<-進(jìn)行數(shù)據(jù)傳遞,channel是golang中一種內(nèi)置基本類(lèi)型,對(duì)于channel操作只有4種方式:
創(chuàng)建channel(通過(guò)make()函數(shù)實(shí)現(xiàn),包括無(wú)緩存channel和有緩存channel);
向channel中添加數(shù)據(jù)(channel<-data);
從channel中讀取數(shù)據(jù)(data<-channel);
關(guān)閉channel(通過(guò)close()函數(shù)實(shí)現(xiàn),關(guān)閉之后無(wú)法再向channel中存數(shù)據(jù),但是可以繼續(xù)從channel中讀取數(shù)據(jù))
channel分為有緩沖channel和無(wú)緩沖channel,兩種channel的創(chuàng)建方法如下:
var ch = make(chan int) //無(wú)緩沖channel,等同于make(chan int ,0)
var ch = make(chan int,10) //有緩沖channel,緩沖大小是5
其中無(wú)緩沖channel在讀和寫(xiě)是都會(huì)阻塞,而有緩沖channel在向channel中存入數(shù)據(jù)沒(méi)有達(dá)到channel緩存總數(shù)時(shí),可以一直向里面存,直到緩存已滿才阻塞.由于阻塞的存在,所以使用channel時(shí)特別注意使用方法,防止死鎖的產(chǎn)生.例子如下:
無(wú)緩存channel:
package mainimport "fmt"func Afuntion(ch chan int) {fmt.Println("finish")<-ch }func main() {ch := make(chan int) //無(wú)緩沖的channelgo Afuntion(ch)ch <- 1// 輸出結(jié)果:// finish }代碼分析:首先創(chuàng)建一個(gè)無(wú)緩沖channel ch, 然后執(zhí)行 go Afuntion(ch),此時(shí)執(zhí)行<-ch,則Afuntion這個(gè)函數(shù)便會(huì)阻塞,不再繼續(xù)往下執(zhí)行,直到主進(jìn)程中ch<-1向channel ch 中注入數(shù)據(jù)才解除Afuntion該協(xié)程的阻塞.
總結(jié):
對(duì)于無(wú)緩存的channel,放入channel和從channel中向外面取數(shù)據(jù)這兩個(gè)操作不能放在同一個(gè)協(xié)程中,防止死鎖的發(fā)生;同時(shí)應(yīng)該先利用go 開(kāi)一個(gè)協(xié)程對(duì)channel進(jìn)行操作,此時(shí)阻塞該go 協(xié)程,然后再在主協(xié)程中進(jìn)行channel的相反操作(與go 協(xié)程對(duì)channel進(jìn)行相反的操作),實(shí)現(xiàn)go 協(xié)程解鎖.即必須go協(xié)程在前,解鎖協(xié)程在后.
帶緩存channel:
對(duì)于帶緩存channel,只要channel中緩存不滿,則可以一直向 channel中存入數(shù)據(jù),直到緩存已滿;同理只要channel中緩存不為0,便可以一直從channel中向外取數(shù)據(jù),直到channel緩存變?yōu)?#xff10;才會(huì)阻塞.
由此可見(jiàn),相對(duì)于不帶緩存channel,帶緩存channel不易造成死鎖,可以同時(shí)在一個(gè)goroutine中放心使用,
close():
close主要用來(lái)關(guān)閉channel通道其用法為close(channel),并且實(shí)在生產(chǎn)者的地方關(guān)閉channel,而不是在消費(fèi)者的地方關(guān)閉.并且關(guān)閉channel后,便不可再想channel中繼續(xù)存入數(shù)據(jù),但是可以繼續(xù)從channel中讀取數(shù)據(jù).例子如下:
package mainimport "fmt"func main() {var ch = make(chan int, 20)for i := 0; i < 10; i++ {ch <- i}close(ch)//ch <- 11 //panic: runtime error: send on closed channelfor i := range ch {fmt.Println(i) //輸出0 1 2 3 4 5 6 7 8 9} }
channel阻塞超時(shí)處理:
goroutine有時(shí)候會(huì)進(jìn)入阻塞情況,那么如何避免由于channel阻塞導(dǎo)致整個(gè)程序阻塞的發(fā)生那?解決方案:通過(guò)select設(shè)置超時(shí)處理,具體程序如下:
package mainimport ("fmt""time" )func main() {c := make(chan int)o := make(chan bool)go func() {for {select {case i := <-c:fmt.Println(i)case <-time.After(time.Duration(3) * time.Second): //設(shè)置超時(shí)時(shí)間為3s,如果channel 3s鐘沒(méi)有響應(yīng),一直阻塞,則報(bào)告超時(shí),進(jìn)行超時(shí)處理.fmt.Println("timeout")o <- truebreak}}}()<-o }
golang 并發(fā)總結(jié):
并發(fā)兩種方式:sync.WaitGroup,該方法最大優(yōu)點(diǎn)是Wait()可以阻塞到隊(duì)列中的所有任務(wù)都執(zhí)行完才解除阻塞,但是它的缺點(diǎn)是不能夠指定并發(fā)協(xié)程數(shù)量.
channel優(yōu)點(diǎn):能夠利用帶緩存的channel指定并發(fā)協(xié)程goroutine,比較靈活.但是它的缺點(diǎn)是如果使用不當(dāng)容易造成死鎖;并且他還需要自己判定并發(fā)goroutine是否執(zhí)行完.
但是相對(duì)而言,channel更加靈活,使用更加方便,同時(shí)通過(guò)超時(shí)處理機(jī)制可以很好的避免channel造成的程序死鎖,因此利用channel實(shí)現(xiàn)程序并發(fā),更加方便,更加易用.
參考文獻(xiàn):
http://studygolang.com/articles/319
http://studygolang.com/articles/267
本文來(lái)自:CSDN博客
感謝作者:chenbaoke
查看原文:golang中并發(fā)sync和channel
總結(jié)
以上是生活随笔為你收集整理的golang中并发sync和channel的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: golang中的sync.WaitGro
- 下一篇: 使用OpenCV开发机器视觉项目