golang 多协程的同步方法总结
之前用 go 寫(xiě)一個(gè)小工具的時(shí)候, 用到了多個(gè)協(xié)程之間的通信, 當(dāng)時(shí)隨手查了查, 結(jié)果查出來(lái)一大坨, 簡(jiǎn)單記錄一下. golang中多個(gè)協(xié)程之間是如何進(jìn)行通信及數(shù)據(jù)同步的嘞.
共享變量
一個(gè)最簡(jiǎn)單, 最容易想到的, 就是通過(guò)全局變量的方式, 多個(gè)協(xié)程讀寫(xiě)同一個(gè)變量. 但對(duì)同一個(gè)變量的更改, 就不得不加鎖了, 否則極易引發(fā)數(shù)據(jù)問(wèn)題. 一般系統(tǒng)庫(kù)都提供基本的鎖, go 也提供了.
package mainimport ("fmt""sync""time" )var num = 0 // 互斥鎖 var mutex = sync.Mutex{} // 讀寫(xiě)鎖 var rwMutex = sync.RWMutex{}func main() {for i := 0; i < 100; i++ {go incrNum()}time.Sleep(2)fmt.Println(num) }func incrNum() {mutex.Lock()num = num + 1mutex.Unlock() }僅執(zhí)行一次
當(dāng)查詢(xún)鎖查到sync這個(gè)模塊時(shí), 發(fā)現(xiàn)它下面的對(duì)象并沒(méi)有幾個(gè), 都是針對(duì)協(xié)程同步的各個(gè)方面給出的解決方案. 所以我就一個(gè)一個(gè)看文檔試了試.
當(dāng)你需要對(duì)環(huán)境, 連接池等等資源進(jìn)行初始化時(shí), 這種操作只需要執(zhí)行一次, 這時(shí)候就需要它了. sync.Once對(duì)象可以保證僅執(zhí)行一次. 和 init 方法有些類(lèi)似, 不過(guò) init 方法是在模塊首次加載時(shí)執(zhí)行, 而sync.Once是在首次調(diào)用時(shí)執(zhí)行. (其實(shí)現(xiàn)就是一個(gè)計(jì)數(shù)器加一個(gè)互斥鎖)
package mainimport ("fmt""sync""time" )var num = 0 var once = sync.Once{}func main() {for i := 0; i < 100; i++ {go once.Do(incrNum)}time.Sleep(2)fmt.Println(num) }func incrNum() {num = num + 1 }等待其他協(xié)程處理
某個(gè)協(xié)程需要等第一階段的所有協(xié)程處理完畢, 才能開(kāi)始執(zhí)行第二階段. 這個(gè)時(shí)候, 等待其他協(xié)程就可以通過(guò)sync.WaitGroup 來(lái)實(shí)現(xiàn). (當(dāng)然, 也可以通過(guò)一個(gè)共享計(jì)數(shù)器變量來(lái)實(shí)現(xiàn)).
package mainimport ("fmt""sync" )var waitGroup = sync.WaitGroup{}func main() {for i := 0; i < 100; i++ {go incrNum()}// 等待其他協(xié)程處理完畢(共享變量為0)waitGroup.Wait()fmt.Println("don") }func incrNum() {// 增加需要等待的協(xié)程數(shù)量(共享變量+1)waitGroup.Add(1)// do something// 標(biāo)記當(dāng)前協(xié)程處理完成(共享變量-1)waitGroup.Done() }消息通知
多個(gè)協(xié)程啟動(dòng)時(shí), 等待某個(gè)命令到來(lái)時(shí)執(zhí)行命令, 喚醒等待協(xié)程. go 對(duì)此類(lèi)操作也進(jìn)行了處理, 感覺(jué)好貼心哦. 但是經(jīng)過(guò)測(cè)試, 即使沒(méi)有空閑的協(xié)程, 喚醒命令同樣能夠發(fā)出去, 所以需要注意一下.
package mainimport ("sync" )var mutex = &sync.Mutex{} var cond = sync.NewCond(mutex)func main() {for i := 0; i < 100; i++ {go incrNum()}// 發(fā)送命令給一個(gè)隨機(jī)獲得鎖的協(xié)程cond.Signal()// 發(fā)送命令給所有獲得鎖的協(xié)程cond.Broadcast() }func incrNum() {// 獲取鎖, 標(biāo)識(shí)當(dāng)前協(xié)程可以處理命令cond.L.Lock()// 可添加退出執(zhí)行命令隊(duì)列的條件for true {// 等待命令cond.Wait()// do something}// 釋放鎖, 標(biāo)記命令處理完畢, 退出協(xié)程cond.L.Unlock() }多協(xié)程 map
普通的 map 在多協(xié)程操作時(shí), 是不支持并發(fā)寫(xiě)入的. go貼心的給封裝了支持并發(fā)寫(xiě)入的map. 同時(shí)也提供了針對(duì)map的基本操作.
package mainimport ("fmt""sync""time" )var m = sync.Map{}func main() {for i := 0; i < 100; i++ {go func() {m.Store("1", 1)}()}time.Sleep(time.Second * 2)// 遍歷 mapm.Range(func(key, value interface{}) bool {// 返回 false 結(jié)束遍歷return true})// 讀取變量, 若不存在則設(shè)置m.LoadOrStore("1", 3)// 刪除 keym.Delete("1")// 讀取變量load, _ := m.Load("1")fmt.Println(load) }多協(xié)程對(duì)象池
對(duì)于數(shù)據(jù)庫(kù)連接池應(yīng)該并不陌生. 而sync.Pool對(duì)象是go封裝的協(xié)程安全的對(duì)象池. 對(duì)象池的使用十分簡(jiǎn)單, 存/取
package mainimport ("sync" )var p = sync.Pool{// 當(dāng)池子中沒(méi)有對(duì)象了, 用于創(chuàng)建新對(duì)象New: func() interface {}{return "3"}, }func main() {// 從池子中獲取一個(gè)對(duì)象r := p.Get()// 用完后將對(duì)象放回池子中p.Put(r) }sync 簡(jiǎn)單總結(jié)
針對(duì)go系統(tǒng)的sync模塊, 提供的基礎(chǔ)功能如下:
幾個(gè)都簡(jiǎn)單試過(guò)之后, 發(fā)現(xiàn)sync模塊針對(duì)常用的幾個(gè)多協(xié)程工具進(jìn)行了封裝, 想來(lái)可以基本滿足日常使用了.
終極通信-channel
channel是一個(gè)協(xié)程安全的通信管道, 簡(jiǎn)單理解為數(shù)據(jù)從一側(cè)放入, 從另一側(cè)拿出. 這玩意感覺(jué)能玩出花來(lái), 還不太理解, 留到國(guó)慶研究.
總結(jié)
以上是生活随笔為你收集整理的golang 多协程的同步方法总结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 简单计算机面试题库及答案_试讲可以看教案
- 下一篇: html5调用系统声音1s响一次_20款