使goroutine同步的方法總結
使goroutine同步的方法總結
前言:
在前面併發效能對比的文章中,我們可以看到Golang處理大併發的能力十分強勁,而且開發也特別方便,只需要用go關鍵字即可開啟一個新的協程。
但當多個goroutine同時進行處理的時候,就會遇到同時搶佔一個資源的情況(併發都會遇到的問題),所以我們希望某個goroutine等待另一個goroutine處理完某一個步驟之後才能繼續。sync包就是為了讓goroutine同步而出現的。當然還可以使用channel實現,這個後面會介紹到。
鎖:
鎖有兩種:互斥鎖(mutex)和讀寫鎖(RWMutex)
互斥鎖: 當資料被加鎖了之後,除次外的其他協程不能對資料進行讀操作和寫操作。 這個當然能解決併發程式對資源的操作。但是,效率上是個問題,因為當加鎖後,其他協程只有等到解鎖後才能對資料進行讀寫操作。
讀寫鎖: 讀資料的時候上讀鎖,寫資料的時候上寫鎖。有寫鎖的時候,資料不可讀不可寫。有讀鎖的時候,資料可讀,不可寫。
兩種鎖的使用方式相同,這裡就只列出互斥鎖的程式碼:
1package main
2
3import (
4 "sync"
5 "time"
6 "fmt"
7)
8
9var num = 0
10
11func main () {
12 mu := &sync.Mutex{}
13 for i:=0;i<10000;i++ {
14 go func(){
15 mu.Lock()
16 defer mu.Unlock()
17 num += 1
18 }()
19 }
20 time.Sleep(time.Second)
21 fmt.Println("num:", num) // 如果不加鎖這裡的num的值會是一個隨機數而不是10000
22}
Once:
有的時候,我們啟動多個相同goroutine,但是裡面的某個操作我只希望被執行一次,這個時候Once就上場了。
1package
1package
2
3
4import (
5 "fmt"
6 "sync"
7 "time"
8)
9
10func main() {
11 var once sync.Once
12 one := func() {
13 fmt.Println("just once")
14 }
15
16 for i := 0; i < 10; i++ {
17 go func(a int) {
18 once.Do(one) // 只是被執行一次
19 }(i)
20 }
21 time.Sleep(time.Millisecond*200)
22}
WaitGroup:
當某個操作或是某個goroutine需要等待一批goroutine執行完畢以後才繼續執行,那麼這種多執行緒(go裡面說的執行緒就是goroutine)等待的問題就可以使用WaitGroup了。
程式碼如下:
1package main
2
3import (
4 "sync"
5 "fmt"
6 "time"
7)
8
9var waitGroup sync.WaitGroup
10
11func main () {
12 for i := 0; i < 10; i++ {
13 waitGroup.Add(1) // 新增需要等待goroutine的數量
14 go func() {
15 fmt.Println("hehe")
16 time.Sleep(time.Second)
17 waitGroup.Done() // 減少需要等待goroutine的數量 相當於Add(-1)
18 } ()
19 }
20
21 waitGroup.Wait() // 執行阻塞,直到所有的需要等待的goroutine數量變成0
22 fmt.Println("over")
23}
Cond:
sync.Cond是用來控制某個條件下,goroutine進入等待時期,等待訊號到來,然後重新啟動。
程式碼如下:
1package main
2
3import (
4 "fmt"
5 "sync"
6 "time"
7)
8var locker = new(sync.Mutex)
9var cond = sync.NewCond(locker)
10
11func test(x int) {
12 cond.L.Lock() //獲取鎖
13 cond.Wait()//等待通知 暫時阻塞
14 fmt.Println(x)
15 time.Sleep(time.Second * 1)
16 cond.L.Unlock()//釋放鎖
17}
18func main() {
19 for i := 0; i < 40; i++ {
20 go test(i)
21 }
22 fmt.Println("start all")
23 time.Sleep(time.Second * 3)
24 fmt.Println("signal1")
25 cond.Signal() // 下發一個通知隨機給已經獲取鎖的goroutine
26 time.Sleep(time.Second * 3)
27 fmt.Println("signal2")
28 cond.Signal()// 下發第二個通知隨機給已經獲取鎖的goroutine
29 time.Sleep(time.Second * 1) // 在廣播之前要等一會,讓所有執行緒都在wait狀態
30 fmt.Println("broadcast")
31 cond.Broadcast()//下發廣播給所有等待的goroutine
32 time.Sleep(time.Second * 60)
33}
上面程式碼有幾個要點要特別說明一下:
1. 每個Cond都必須有個與之關聯的鎖 // 見第9行
2. 協程方法裡面一開始/結束都必須加/解鎖 // 見第12行和16行
3. cond.Wait()時會自動解鎖,當被喚醒時,又會加上鎖。所以第2點提到必須加/解鎖。
Channel
channel不僅可以用來goroutine之間的通訊,也可以使goroutine同步完成協作。這點主要基於從channel取資料的時候,會阻塞當前goroutine這個特性。示例程式碼如下:
1package main
2
3import (
4 "fmt"
5 "time"
6)
7
8
9var chan1 = make(chan string, 512)
10
11var arr1 = []string{"qq","ww","ee","rr","tt"}
12
13func chanTest1() {
14 for _, v := range arr1 {
15 chan1 <- v
16 }
17 close(chan1) // 關閉channel
18}
19
20func chanTest2() {
21 for {
22 getStr, ok := <- chan1 // 阻塞,直到chan1裡面有資料
23 if !ok { // 判斷channel是否關閉或者為空
24 return
25 }
26 fmt.Println(getStr) // 按陣列順序內容輸出
27 }
28}
29
30func main () {
31 go chanTest1()
32 go chanTest2()
33
34 time.Sleep(time.Millisecond*200)
35}
原文釋出時間為:2018-10-8
本文來自雲棲社群合作伙伴“Golang語言社群”,瞭解相關資訊可以關注“Golang語言社群”。
相關文章
- golang 多協程的同步方法總結Golang
- goroutine間的同步&協作Go
- Vue 中 Promise 的then方法非同步使用及async/await 非同步使用總結VuePromise非同步AI
- php 非同步上傳圖片幾種方法總結PHP非同步
- 線上MYSQL同步報錯故障處理方法總結MySql
- LocalDateTime的方法總結LDA
- JS中的非同步操作總結JS非同步
- springboot使controller非同步呼叫Spring BootController非同步
- Flutter 中的非同步程式設計總結Flutter非同步程式設計
- JS-Array的方法總結JS
- Laravel 中 faker 的方法總結Laravel
- Javascript非同步程式設計總結JavaScript非同步程式設計
- 學會使用context取消goroutine執行的方法ContextGo
- 總結非同步程式設計的六種方式非同步程式設計
- 知識方法總結
- 傳統方法總結
- Dapper常用方法總結APP
- 常用js方法總結:JS
- js字串方法總結JS字串
- String方法小總結
- 設計用例的方法總結
- 總結前端效能優化的方法前端優化
- 陣列中常用的方法總結陣列
- JAVASE常用的類及其方法總結Java
- 所有陣列的方法(api)總結陣列API
- .Net防sql注入的方法總結SQL
- Java-String的常用方法總結!Java
- 內網穿透的常用方法總結內網穿透
- PHP的效能優化方法總結PHP優化
- PHP實現非同步操作總結PHP非同步
- JS中的call()方法和apply()方法用法總結JSAPP
- Android使背景燈(Brightness)高亮的方法Android
- web測試方法總結Web
- Javascript Object常用方法總結JavaScriptObject
- 前端跨域方法總結前端跨域
- javascript遍歷方法總結JavaScript
- Object物件常用方法總結Object物件
- JS遍歷方法總結JS