最近学习了Go语言中同步包中的互斥锁、读写锁、Once、waitGroup。在并发程序开发的过程中,这两种锁是非常重要的,包括对共享资源进行访问控制的时候。sync是Go语言中的标准库。
Mutex 互斥锁
- 互斥锁是传统并发程序对共享资源进行访问控制的主要手段。是sync包中的Mutex结构体。
- 该结构体包括了两个方法,可以说是非常简单使用的
func (m *Mutex) Lock() {}
func (m *Mutex) Unlock() {}
- 我们通过一个简单的例子来说明他的用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| package main import ( "sync" "fmt" ) type safeCounter struct { number int sync.Mutex } func (sc *safeCounter) Increment() { sc.Lock() sc.number++ sc.Unlock() } func (sc *safeCounter) Decrement() { sc.Lock() sc.number-- sc.Unlock() } func (sc *safeCounter) getNumber() int { sc.Lock() number := sc.number sc.Unlock() return number } func main() { sc := new(safeCounter) for i := 0; i < 100; i++ { go sc.Increment() go sc.Decrement() } fmt.Println(sc.getNumber()) }
|
- 上述例子通过互斥锁来保证number整型变量在使用goroutine的情况下,持续稳定地输出0的结果。当然你可以试一下,去掉锁的保护,是否还可以稳定输出0?
RWMutex 读写锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| package main import ( "fmt" "math/rand" "sync" "time" ) type MapCounter struct { m map[int]int sync.RWMutex } func (mapCounter *MapCounter) Reader(n int) { for { mapCounter.RLock() v := mapCounter.m[rand.Intn(n)] mapCounter.RUnlock() fmt.Println(v) time.Sleep(1 * time.Second) } } func (mapCounter *MapCounter) Writer(n int) { for i := 0; i < n; i++ { mapCounter.Lock() mapCounter.m[i] = i * 10 mapCounter.Unlock() time.Sleep(1 * time.Second) } } func main() { mc := MapCounter{m: make(map[int]int)} go mc.Writer(10) go mc.Reader(10) go mc.Reader(10) time.Sleep(15 * time.Second) }
|
Once
- Once 顾名思义,就是只执行一次 。通过一个简单的例子就可以说明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| package main import ( "fmt" "sync" ) func main() { once := sync.Once{} done := make(chan bool) for i := 0; i < 10; i++ { go func() { once.Do(sayHello) done <- true }() } for i := 0; i < 10; i++ { fmt.Println(<-done) } } func sayHello() { fmt.Println("Hello") }
|
- 以上的例子只输出了一个“Hello” ,其余的都是true。
WaitGroup
- WaitGroup 是可以一个goroutine集合,当集合里的goroutine执行完毕后,这个WaitGroup就会自动结束。你可以定义这个WaitGroup的大小。他里面只有三个方法:
- func (wg *WaitGroup) Add(delta int) {} //定义大小
- func (wg *WaitGroup) Done() {}
- func (wg *WaitGroup) Wait() {}
- 具体用法,通过下面的小例子说明。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package main import ( "fmt" "sync" ) func main() { wg := sync.WaitGroup{} for i := 0; i <= 5; i++ { wg.Add(1) go func(i int) { defer wg.Done() fmt.Println("Work done for ", i) }(i) } wg.Wait() }
|
- 建议大家可以把上面的代码都跑一遍,方便自己理解代码的含义。结合官方文档来理解会更加容易。
参考
有疑问加站长微信联系(非本文作者)