掃二維碼與項目經(jīng)理溝通
我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
Go語言中 sync 包里提供了互斥鎖 Mutex 和讀寫鎖 RWMutex 用于處理并發(fā)過程中可能出現(xiàn)同時兩個或多個協(xié)程(或線程)讀或?qū)懲粋€變量的情況。

創(chuàng)新互聯(lián)是一家集網(wǎng)站建設(shè),金沙企業(yè)網(wǎng)站建設(shè),金沙品牌網(wǎng)站建設(shè),網(wǎng)站定制,金沙網(wǎng)站建設(shè)報價,網(wǎng)絡(luò)營銷,網(wǎng)絡(luò)優(yōu)化,金沙網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競爭力??沙浞譂M足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時我們時刻保持專業(yè)、時尚、前沿,時刻以成就客戶成長自我,堅持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實用型網(wǎng)站。
鎖是 sync 包中的核心,它主要有兩個方法,分別是加鎖(Lock)和解鎖(Unlock)。
在并發(fā)的情況下,多個線程或協(xié)程同時其修改一個變量,使用鎖能保證在某一時間內(nèi),只有一個協(xié)程或線程修改這一變量。
不使用鎖時,在并發(fā)的情況下可能無法得到想要的結(jié)果,如下所示:
package main
import (
"fmt"
"time"
)
func main() {
var a = 0
for i := 0; i < 1000; i++ {
go func(idx int) {
a += 1
fmt.Println(a)
}(i)
}
time.Sleep(time.Second)
}從理論上來說,上面的程序會將 a 的值依次遞增輸出,然而實際結(jié)果卻是下面這樣子的。
537
995
996
997
538
999
1000
通過運行結(jié)果可以看出 a 的值并不是按順序遞增輸出的,這是為什么呢?
協(xié)程的執(zhí)行順序大致如下所示:
按照上面的順序,假如有一個協(xié)程取得 a 的值為 3,然后執(zhí)行加法運算,此時又有一個協(xié)程對 a 進(jìn)行取值,得到的值同樣是 3,最終兩個協(xié)程的返回結(jié)果是相同的。
而鎖的概念就是,當(dāng)一個協(xié)程正在處理 a 時將 a 鎖定,其它協(xié)程需要等待該協(xié)程處理完成并將 a 解鎖后才能再進(jìn)行操作,也就是說同時處理 a 的協(xié)程只能有一個,從而避免上面示例中的情況出現(xiàn)。
上面的示例中出現(xiàn)的問題怎么解決呢?加一個互斥鎖 Mutex 就可以了。那什么是互斥鎖呢 ?互斥鎖中其有兩個方法可以調(diào)用,如下所示:
func (m *Mutex) Lock()
func (m *Mutex) Unlock()
將上面的代碼略作修改,如下所示:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var a = 0
var lock sync.Mutex
for i := 0; i < 1000; i++ {
go func(idx int) {
lock.Lock()
defer lock.Unlock()
a += 1
fmt.Printf("goroutine %d, a=%d\n", idx, a)
}(i)
}
// 等待 1s 結(jié)束主程序
// 確保所有協(xié)程執(zhí)行完
time.Sleep(time.Second)
}運行結(jié)果如下:
goroutine 995, a=996
goroutine 996, a=997
goroutine 997, a=998
goroutine 998, a=999
goroutine 999, a=1000
需要注意的是一個互斥鎖只能同時被一個 goroutine 鎖定,其它 goroutine 將阻塞直到互斥鎖被解鎖(重新爭搶對互斥鎖的鎖定),示例代碼如下:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
ch := make(chan struct{}, 2)
var l sync.Mutex
go func() {
l.Lock()
defer l.Unlock()
fmt.Println("goroutine1: 我會鎖定大概 2s")
time.Sleep(time.Second * 2)
fmt.Println("goroutine1: 我解鎖了,你們?nèi)尠?)
ch <- struct{}{}
}()
go func() {
fmt.Println("goroutine2: 等待解鎖")
l.Lock()
defer l.Unlock()
fmt.Println("goroutine2: 歐耶,我也解鎖了")
ch <- struct{}{}
}()
// 等待 goroutine 執(zhí)行結(jié)束
for i := 0; i < 2; i++ {
<-ch
}
} 上面的代碼運行結(jié)果如下:
goroutine1: 我會鎖定大概 2s
goroutine2: 等待解鎖
goroutine1: 我解鎖了,你們?nèi)尠?br /> goroutine2: 歐耶,我也解鎖了
讀寫鎖有如下四個方法:
func (*RWMutex) Lock和func (*RWMutex) Unlock;func (*RWMutex) Rlock和func (*RWMutex) RUnlock。讀寫鎖的區(qū)別在于:
所以說這里的讀鎖定(RLock)目的其實是告訴寫鎖定,有很多協(xié)程或者進(jìn)程正在讀取數(shù)據(jù),寫操作需要等它們讀(讀解鎖)完才能進(jìn)行寫(寫鎖定)。
我們可以將其總結(jié)為如下三條:
示例代碼如下所示:
package main
import (
"fmt"
"math/rand"
"sync"
)
var count int
var rw sync.RWMutex
func main() {
ch := make(chan struct{}, 10)
for i := 0; i < 5; i++ {
go read(i, ch)
}
for i := 0; i < 5; i++ {
go write(i, ch)
}
for i := 0; i < 10; i++ {
<-ch
}
}
func read(n int, ch chan struct{}) {
rw.RLock()
fmt.Printf("goroutine %d 進(jìn)入讀操作...\n", n)
v := count
fmt.Printf("goroutine %d 讀取結(jié)束,值為:%d\n", n, v)
rw.RUnlock()
ch <- struct{}{}
}
func write(n int, ch chan struct{}) {
rw.Lock()
fmt.Printf("goroutine %d 進(jìn)入寫操作...\n", n)
v := rand.Intn(1000)
count = v
fmt.Printf("goroutine %d 寫入結(jié)束,新值為:%d\n", n, v)
rw.Unlock()
ch <- struct{}{}
} 其執(zhí)行結(jié)果如下:
goroutine 0 進(jìn)入讀操作...
goroutine 0 讀取結(jié)束,值為:0
goroutine 3 進(jìn)入讀操作...
goroutine 1 進(jìn)入讀操作...
goroutine 3 讀取結(jié)束,值為:0
goroutine 1 讀取結(jié)束,值為:0
goroutine 4 進(jìn)入寫操作...
goroutine 4 寫入結(jié)束,新值為:81
goroutine 4 進(jìn)入讀操作...
goroutine 4 讀取結(jié)束,值為:81
goroutine 2 進(jìn)入讀操作...
goroutine 2 讀取結(jié)束,值為:81
goroutine 0 進(jìn)入寫操作...
goroutine 0 寫入結(jié)束,新值為:887
goroutine 1 進(jìn)入寫操作...
goroutine 1 寫入結(jié)束,新值為:847
goroutine 2 進(jìn)入寫操作...
goroutine 2 寫入結(jié)束,新值為:59
goroutine 3 進(jìn)入寫操作...
goroutine 3 寫入結(jié)束,新值為:81
下面再來看兩個示例。
【示例 1】多個讀操作同時讀取一個變量時,雖然加了鎖,但是讀操作是不受影響的。(讀和寫是互斥的,讀和讀不互斥)
package main
import (
"sync"
"time"
)
var m *sync.RWMutex
func main() {
m = new(sync.RWMutex)
// 多個同時讀
go read(1)
go read(2)
time.Sleep(2*time.Second)
}
func read(i int) {
println(i,"read start")
m.RLock()
println(i,"reading")
time.Sleep(1*time.Second)
m.RUnlock()
println(i,"read over")
} 運行結(jié)果如下:
1 read start
1 reading
2 read start
2 reading
1 read over
2 read over
【示例 2】由于讀寫互斥,所以寫操作開始的時候,讀操作必須要等寫操作進(jìn)行完才能繼續(xù),不然讀操作只能繼續(xù)等待。
package main
import (
"sync"
"time"
)
var m *sync.RWMutex
func main() {
m = new(sync.RWMutex)
// 寫的時候啥也不能干
go write(1)
go read(2)
go write(3)
time.Sleep(2*time.Second)
}
func read(i int) {
println(i,"read start")
m.RLock()
println(i,"reading")
time.Sleep(1*time.Second)
m.RUnlock()
println(i,"read over")
}
func write(i int) {
println(i,"write start")
m.Lock()
println(i,"writing")
time.Sleep(1*time.Second)
m.Unlock()
println(i,"write over")
} 運行結(jié)果如下:
1 write start
3 write start
1 writing
2 read start
1 write over
2 reading

我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流