golang中併發sync和channel

2021-07-22 09:12:18 字數 4113 閱讀 9008

golang中實現併發非常簡單,只需在需要併發的函式前面新增關鍵字"go",但是如何處理go併發機制中不同goroutine之間的同步與通訊,golang 中提供了sync包和channel機制來解決這一問題.

sync 包提供了互斥鎖這類的基本的同步原語.除 once 和 waitgroup 之外的型別大多用於底層庫的例程。更高階的同步操作通過通道與通訊進行。

type cond

func newcond(l locker) *cond

func (c *cond) broadcast()

func (c *cond) signal()

func (c *cond) wait()

type locker

type mutex

func (m *mutex) lock()

func (m *mutex) unlock()

type once

func (o *once) do(f func())

type pool

func (p *pool) get() inte***ce{}

func (p *pool) put(x inte***ce{})

type rwmutex

func (rw *rwmutex) lock()

func (rw *rwmutex) rlock()

func (rw *rwmutex) rlocker() locker

func (rw *rwmutex) runlock()

func (rw *rwmutex) unlock()

type waitgroup

func (wg *waitgroup) add(delta int)

func (wg *waitgroup) done()

func (wg *waitgroup) wait()

而golang中的同步是通過sync.waitgroup來實現的.waitgroup的功能:它實現了乙個類似佇列的結構,可以一直向佇列中新增任務,當任務完成後便從佇列中刪除,如果佇列中的任務沒有完全完成,可以通過wait()函式來出發阻塞,防止程式繼續進行,直到所有的佇列任務都完成為止.

waitgroup總共有三個方法:add(delta int), done(), wait()。

add:新增或者減少等待goroutine的數量

done:相當於add(-1)

wait:執行阻塞,直到所有的waitgroup數量變成0

具體例子如下:

package main

import (

"fmt"

"sync"

)var waitgroup sync.waitgroup

func afunction(shownum int)

func main()

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 main

import "fmt"

func afuntion(ch chan int)

func main()

**分析:首先建立乙個無緩衝channel ch, 然後執行 go afuntion(ch),此時執行<-ch,則afuntion這個函式便會阻塞,不再繼續往下執行,直到主程序中ch<-1向channel ch 中注入資料才解除afuntion該協程的阻塞.

package main

import "fmt"

func afuntion(ch chan int)

func main()

**分析:首先建立乙個無緩衝的channel, 然後在主協程裡面向channel ch 中通過ch<-1命令寫入資料,則此時主協程阻塞,就無法執行下面的go afuntions(ch),自然也就無法解除主協程的阻塞狀態,則系統死鎖

總結:對於無快取的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 main

import "fmt"

func main()

close(ch)

//ch <- 11 //panic: runtime error: send on closed channel

for i := range ch

}

channel阻塞超時處理:

goroutine有時候會進入阻塞情況,那麼如何避免由於channel阻塞導致整個程式阻塞的發生那?解決方案:通過select設定超時處理,具體程式如下:

package main

import (

"fmt"

"time"

)func main()

}}()

<-o

}

golang 併發總結:

併發兩種方式:sync.waitgroup,該方法最大優點是wait()可以阻塞到佇列中的所有任務都執行完才解除阻塞,但是它的缺點是不能夠指定併發協程數量.

channel優點:能夠利用帶快取的channel指定併發協程goroutine,比較靈活.但是它的缺點是如果使用不當容易造成死鎖;並且他還需要自己判定併發goroutine是否執行完.

但是相對而言,channel更加靈活,使用更加方便,同時通過超時處理機制可以很好的避免channel造成的程式死鎖,因此利用channel實現程式併發,更加方便,更加易用.

golang中併發sync和channel

golang中實現併發非常簡單,只需在需要併發的函式前面新增關鍵字 go 但是如何處理go併發機制中不同goroutine之間的同步與通訊,golang 中提供了sync包和channel機制來解決這一問題 sync 包提供了互斥鎖這類的基本的同步原語.除 once 和 waitgroup 之外的型...

golang中併發sync和channel

golang中實現併發非常簡單,只需在需要併發的函式前面新增關鍵字 go 但是如何處理go併發機制中不同goroutine之間的同步與通訊,golang 中提供了sync包和channel機制來解決這一問題 sync 包提供了互斥鎖這類的基本的同步原語.除 once 和 waitgroup 之外的型...

golang 之sync 併發安全鎖

讓乙個程式併發安全並不需要其中的每乙個具體型別都是併發安全的。實際上併發安全的型別其實是特例而不是普遍存在的,所以僅在文件指出型別是安全的情況下,才可以併發的訪問乙個變數。與之對應的是,匯出的包級別函式通常可以認為是併發安全的。因為包級別的變數無法限制在乙個goroutine內。所以那些修改這些變數...