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