首页IT科技go语言 cgo(《Go 语言并发之道》读书笔记(三))

go语言 cgo(《Go 语言并发之道》读书笔记(三))

时间2025-08-05 08:02:06分类IT科技浏览5302
导读:今天这篇笔记我们来学习锁:互斥锁(Mutex 和 读写锁(RWMutex 互斥锁(Mutex ...

今天这篇笔记我们来学习锁:互斥锁(Mutex) 和 读写锁(RWMutex)

互斥锁(Mutex)

首先我们来看一段代码                ,没有加锁的情况下                      ,两个goroutine同时修改一个变量        ,会发生什么

func main() { var count int increment := func() { count++ fmt.Printf(" Incrementing: %d \n", count) } decrement := func() { count-- fmt.Printf(" Decrementing: %d \n", count) } var arithmetic sync.WaitGroup for i := 0; i <= 5; i++ { arithmetic.Add(1) go func() { defer arithmetic.Done() increment() }() } for i := 0; i <= 5; i++ { arithmetic.Add(1) go func() { defer arithmetic.Done() decrement() }() } arithmetic.Wait() fmt.Println("Arithmetic complete.") }

上面的代码定义了一个increment方法和一个decrement方法            ,他们都操作count变量                      , 然后各自启动5个goutinue去调用这两个方法              。 结果如下所示

Decrementing: 1 Incrementing: 2 Incrementing: 1 Decrementing: 0 Decrementing: -1 Decrementing: -1 Incrementing: 0 Decrementing: -2 Decrementing: -3 Incrementing: -2 Incrementing: -1 Incrementing: 0 Arithmetic complete.

我们可以看到            ,结果是乱的        ,第一个decrementing 应该是-1                      ,结果这里输出了1               ,第三个incrementing应该是3    ,结果输出是1. 这样的效果肯定不是我们期望的                       , 当多个goroutine共享一个变量的时候                  ,我们需要加锁,保证一次只有一个goroutine能够拿到锁                        。如下代码

func main() { var count int var lock sync.Mutex increment := func() { lock.Lock() defer lock.Unlock() count++ fmt.Printf(" Incrementing: %d \n", count) } decrement := func() { lock.Lock() defer lock.Unlock() count-- fmt.Printf(" Decrementing: %d \n", count) } var arithmetic sync.WaitGroup for i := 0; i <= 5; i++ { arithmetic.Add(1) go func() { defer arithmetic.Done() increment() }() } for i := 0; i <= 5; i++ { arithmetic.Add(1) go func() { defer arithmetic.Done() decrement() }() } arithmetic.Wait() fmt.Println("Arithmetic complete.") }

我们在方法中加了lock.Lock()和defer lock.Unlock()                    , 运行的效果如下图

Incrementing: 1 Incrementing: 2 Incrementing: 3 Incrementing: 4 Incrementing: 5 Decrementing: 4 Decrementing: 3 Decrementing: 2 Decrementing: 1 Decrementing: 0 Decrementing: -1 Incrementing: 0

这样的结果符合我们的预期                      , incrementing的时候和上一条比加了1    , decrementing的时候和上一条比减少了1                ,代码改动是有效的                      ,这就是锁的作用        , 加锁后保证一次只有一个goroutine访问共享的变量        。

读写锁(RWMutex)

什么是读写锁呢? 读写锁允许多个只读操作并行进行            ,而写操作会完全互斥          。 还是使用上面的例子                      ,假如我有个方法只是想读取count的value            ,并不改变它        ,那么我们就可以用RWMutex.

我们稍微改变下上面的代码 func main() { var count int var lock sync.RWMutex increment := func() { lock.Lock() defer lock.Unlock() count++ fmt.Printf(" Incrementing: %d \n", count) } decrement := func() { lock.Lock() defer lock.Unlock() count-- fmt.Printf(" Decrementing: %d %d\n", count, time.Now().Nanosecond()) time.Sleep(time.Second) } read := func() { lock.RLock() defer lock.RUnlock() fmt.Printf(" reading: %d %d\n", count, time.Now().Nanosecond()) time.Sleep(time.Second) } var arithmetic sync.WaitGroup for i := 0; i <= 5; i++ { arithmetic.Add(1) go func() { defer arithmetic.Done() increment() }() } for i := 0; i <= 5; i++ { arithmetic.Add(1) go func() { defer arithmetic.Done() decrement() }() } for i := 0; i <= 10; i++ { arithmetic.Add(1) go func() { defer arithmetic.Done() read() }() } arithmetic.Wait() fmt.Println("Arithmetic complete.") }

两个改动                      ,将Mutex换成RWMutex               , 增加了一个read方法    ,它只读取count变量                       ,它加锁的方法是lock.RLock(), 同时我们故意加了time.Sleep(time.Second)                  ,让read方法和decrement方法执行的时候,停顿一下                        。

执行结果如下所示 Incrementing: 1 reading: 1 177230700 reading: 1 177230700 reading: 1 177230700 reading: 1 177230700 reading: 1 177230700 reading: 1 177230700 reading: 1 177230700 reading: 1 177230700 reading: 1 177230700 reading: 1 177230700 reading: 1 177230700 Incrementing: 2 Incrementing: 3 Incrementing: 4 Incrementing: 5 Decrementing: 4 193430000 Decrementing: 3 204475300 Decrementing: 2 213426500 Decrementing: 1 227715200 Decrementing: 0 240186800 Decrementing: -1 255299900 Incrementing: 0 Arithmetic complete.

我们可以看到read方法                    ,几个gorountine执行的时间几乎一样                      ,他们都能拿到读锁    ,不会被阻塞                , 而且它拿到了一个准确的当时的value. 而decrement方法                      ,相同的代码        ,我们使用的是写锁            ,不同的gorountine会锁住                      ,他们的执行时间会相差            。 这就是读写锁      。

拿读锁我们还可以用RWMutex.RLocker()来拿到锁对象sync.Locker                       。

书中还比较了一个RWMutex和Mutex的性能差异            ,

当Reader数量比较小(<8)时        ,RWMutex性能稍差

当Reader数量大于8小于65536的时候                      , RWMutex比Mutex快一倍

当Reader数量大于131072的时候               ,RWMutex又比Mutex稍慢

作者说RWMutex要比Mutex稍复杂    ,所以会有这样的结果

作者在书中说:通常建议使用RWMutex                       ,而不是Mutex                  , 因为它在逻辑上更合理                。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
ps如何抠图换背景图形(ps教程之如何抠图换背景)