golang中的锁分为互斥锁、读写锁、原子锁即原子操作。
在 Golang 里有专门的方法来实现锁,就是 sync 包,这个包有两个很重要的锁类型。一个叫 Mutex, 利用它可以实现互斥锁。
一个叫 RWMutex,利用它可以实现读写锁。
将上面的结论展开一下,更清晰得说(为避免理解偏差宁可唠叨一些):
否则,会 panic fatal error: all goroutines are asleep - deadlock!
var l sync.RWMutex func lockAndRead() { // 可读锁内使用可读锁 l.RLock() defer l.RUnlock() l.RLock() defer l.RUnlock() } func main() { lockAndRead() time.Sleep(5 * time.Second) }
而将 lockAndRead 换为以下三种函数均会造成 panic:
func lockAndRead1() { // 全局锁内使用全局锁 l.Lock() defer l.Unlock() l.Lock() defer l.Unlock() } func lockAndRead2() { // 全局锁内使用可读锁 l.Lock() defer l.Unlock() // 由于 defer 是栈式执行,所以这两个锁是嵌套结构 l.RLock() defer l.RUnlock() } func lockAndRead3() { // 可读锁内使用全局锁 l.RLock() defer l.RUnlock() l.Lock() defer l.Unlock() }
互斥锁有两个方法:加锁、解锁。
一个互斥锁只能同时被一个 goroutine 锁定,其它 goroutine 将阻塞直到互斥锁被解锁(重新争抢对互斥锁的锁定)。
使用Lock加锁后,不能再进行加锁,只有当对其进行Unlock解锁之后,才能对其加锁。这个很好理解。
func (m *Mutex) Lock() func (m *Mutex) Unlock()
读写锁有四个方法:读的加锁、解锁,写的加锁、解锁。
func (*RWMutex)Lock() func (*RWMutex)Unlock()
和
func (*RWMutex)RLock() func (*RWMutex)RUnlock()
RWMutex的使用主要事项
type MyMutex struct { count int sync.Mutex } func main() { var mu MyMutex mu.Lock() var mu1 = mu mu.count++ mu.Unlock() mu1.Lock() mu1.count++ mu1.Unlock() fmt.Println(mu.count, mu1.count) }
加锁后复制变量,会将锁的状态也复制,所以 mu1 其实是已经加锁状态,再加锁会死锁
加上 -race 参数验证数据竞争
以下代码有什么问题,怎么解决?
func main() { total, sum := 0, 0 for i := 1; i <= 10; i++ { sum += i go func() { total += i }() } fmt.Printf("total:%d sum %d", total, sum) }
该题的第二个考点:data race。
因为存在多 goroutine 同时写 total 变量的问题,所以有数据竞争。
可以加上 -race 参数验证
go run -race main.go ================== WARNING: DATA RACE Read at 0x00c0001b4020 by goroutine 8: main.main.func1() /Users/xuxinhua/main.go:12 +0x57 Previous write at 0x00c0001b4020 by main goroutine: main.main() /Users/xuxinhua/main.go:9 +0x10b Goroutine 8 (running) created at: main.main() /Users/xuxinhua/main.go:11 +0xe7 ==================
正确答案
package main import ( "sync/atomic" "sync" "fmt" ) func main() { var wg sync.WaitGroup var total int64 sum := 0 for i := 1; i <= 10; i++ { wg.Add(1) sum += i go func(i int) { defer wg.Done() atomic.AddInt64(&total, int64(i)) }(i) } wg.Wait() fmt.Printf("total:%d sum %d", total, sum) }
以上为个人经验,希望能给大家一个参考,也希望大家多多支持。