在前面我们已经讲了如何使用 channel 在多个 goroutine 之间进行通信,其实对于并发还有一种较为常用通信方式,那就是共享内存。
首先我们来看一个例子:
package main import ( "log" "time" ) var name string func main() { name = "小明" go printName() go printName() time.Sleep(time.Second) name = "小红" go printName() go printName() time.Sleep(time.Second) } func printName() { log.Println("name is", name) }
运行程序,可以得到类似输出结果:
2018/03/23 14:53:28 name is 小明 2018/03/23 14:53:28 name is 小明 2018/03/23 14:53:29 name is 小红 2018/03/23 14:53:29 name is 小红
可以看到在两个 goroutine 中我们都可以访问 name 这个变量,当修改它后,在不同的 goroutine 中都可以同时获取到最新的值。
name
这就是一个最简单的通过共享变量(内存)的方式在多个 goroutine 进行通信的方式。
下面再来看一个例子:
package main import ( "fmt" "sync" ) func main() { var ( wg sync.WaitGroup numbers []int ) for i := 0; i < 10; i++ { wg.Add(1) go func(i int) { numbers = append(numbers, i) wg.Done() }(i) } wg.Wait() fmt.Println("The numbers is", numbers) }
多次运行代码,可以得到类似输出:
The numbers is [0 1 5 4 7] The numbers is [0 5 7]
可以看到当我们并发对同一个切片进行写操作的时候,会出现数据不一致的问题,这就是一个典型的共享变量的问题。
针对这个问题我们可以使用 Lock(锁)来修复,从而保证数据的一致性,例如:
package main import ( "fmt" "sync" ) func main() { var ( wg sync.WaitGroup numbers []int mux sync.Mutex ) for i := 0; i < 10; i++ { wg.Add(1) go func(i int) { mux.Lock() numbers = append(numbers, i) mux.Unlock() wg.Done() }(i) } wg.Wait() fmt.Println("The numbers is", numbers) }
修改过后,我们再次运行代码,可以看到最后的 numbers 都会包含 0~9 这个10个数字。
sync.Mutex 是互斥锁,只有一个信号标量;在 Go 中还有一种读写锁 sync.RWMutex,对于我们的共享对象,如果可以分离出读和写两个互斥信号的情况,可以考虑使用它来提高读的并发性能。
sync.Mutex
sync.RWMutex
例如代码:
package main import ( "fmt" "sync" "sync/atomic" "time" ) func main() { var ( mux sync.Mutex state1 = map[string]int{ "a": 65, } muxTotal uint64 rw sync.RWMutex state2 = map[string]int{ "a": 65, } rwTotal uint64 ) for i := 0; i < 10; i++ { go func() { for { mux.Lock() _ = state1["a"] mux.Unlock() atomic.AddUint64(&muxTotal, 1) } }() } for i := 0; i < 10; i++ { go func() { for { rw.RLock() _ = state2["a"] rw.RUnlock() atomic.AddUint64(&rwTotal, 1) } }() } time.Sleep(time.Second) fmt.Println("sync.Mutex readOps is", muxTotal) fmt.Println("sync.RWMutex readOps is", rwTotal) }
运行代码可以得到如下结果:
sync.Mutex readOps is 1561870 sync.RWMutex readOps is 15651069
可以看到使用 sync.RWMutex 的读的并发能力大概是 sync.Mutex 的十倍,从而大大提高了其并发能力。
Lock
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8