Golang

关注公众号 jb51net

关闭
首页 > 脚本专栏 > Golang > Go sync.Mutex 加锁失效

Go中sync.Mutex 加锁失效的问题解决

作者:Looooking

sync.Mutex是Go标准库中常用的一个排外锁,本文主要介绍了Go中sync.Mutex 加锁失效的问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

我先声明一下,并不是真的加锁失效,而是我之前的理解有误,导致看起来像是加锁失效一样。于是乎记录一下,加深一下印象。

我之前有个理解误区(不知道大家有没有,有的话赶紧纠正一下——其实也是因为我这块的知识掌握不牢固导致的):觉得只要是加锁后,在我主动调用解锁之前,这个块范围内的变量一定不会被其他地方修改。后来验证发现,我大错特错了。

起因

最近在学习 sync.Mutex 加锁时,写了下面一段代码进行练习。

package main

import (
	"fmt"
	"sync"
	"time"
)


type Info struct {
	mu sync.Mutex
	Value string
}

func Update(info *Info) {
	fmt.Printf("%s: before update. Value: %s\n", time.Now().Format(timeFormat), info.Value)
	info.mu.Lock()
	defer info.mu.Unlock()
	time.Sleep(2 * time.Second)
	fmt.Printf("%s: in update. Value: %s\n", time.Now().Format(timeFormat), info.Value)
	info.Value = "update"
	fmt.Printf("%s: after update. Value: %s\n", time.Now().Format(timeFormat), info.Value)
}

const timeFormat = "2006-01-02 15:04:05"

func main() {
	fmt.Printf("%s: main start\n", time.Now().Format(timeFormat))
	info := &Info{}
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer wg.Done()
		Update(info)
	}()
	time.Sleep(time.Second)
	info.Value = "main"
	fmt.Printf("%s: in main. Value: %s\n", time.Now().Format(timeFormat), info.Value)
	wg.Wait()
}

按照我原先上面的理解,Update() 函数当中,before update 和 in update 中对应结构体的值应该是不会变的(毕竟我加了锁)。然而从运行结果发现,Update() 函数执行期间,结构体变量的 Value 竟然还是被外部主线程修改了。

分析

那么为什么会这样呢?明明 Update() 里边已经添加了锁,为什么执行期间还是会被其他地方修改呢?

最后发现,究其原因,还是在于主线程修改变量的值的时候,没有先判断锁 mu 是否已经释放,就直接进行了修改操作。

主线程中加上获取锁的操作后,会先判断当前锁是否被释放,如果没被释放,就会一直进行等待直到锁释放后才继续执行后面的操作。

输出结果也和预期保持一致 。

2024-04-16 23:59:13: main start
2024-04-16 23:59:13: before update. Value: 
2024-04-16 23:59:15: in update. Value: 
2024-04-16 23:59:15: after update. Value: update
2024-04-16 23:59:15: in main. Value: main

正常来说,锁是要配合多 goroutine 来使用的, 对于单线程来说,由于没有其他线程进行资源竞争,加锁的意义不大;对于多 goroutine 而言,对于获取和释放锁的时机,应该由应用程序合理控制。关于锁的使用,还有一些其他注意事项,这块也一并写一下。

缓冲通道实现互斥逻辑

当然,我们还可以通过缓冲为1的通道实现互斥锁的逻辑。

package main

import (
	"fmt"
	"sync"
	"time"
)


type Info struct {
	Value string
}

func Update(info *Info, sem chan bool) {
	fmt.Printf("%s: before update. Value: %s\n", time.Now().Format(timeFormat), info.Value)
	sem <- true
	defer func() {
		<- sem
	}()
	time.Sleep(2 * time.Second)
	fmt.Printf("%s: in update. Value: %s\n", time.Now().Format(timeFormat), info.Value)
	info.Value = "update"
	fmt.Printf("%s: after update. Value: %s\n", time.Now().Format(timeFormat), info.Value)
}

const timeFormat = "2006-01-02 15:04:05"

func main() {
	sem := make(chan bool, 1)
	fmt.Printf("%s: main start\n", time.Now().Format(timeFormat))
	info := &Info{}
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer wg.Done()
		Update(info, sem)
	}()
	time.Sleep(time.Second)
	sem <- true
	info.Value = "main"
	fmt.Printf("%s: in main. Value: %s\n", time.Now().Format(timeFormat), info.Value)
	<- sem
	wg.Wait()
}

2024-04-17 00:26:25: main start
2024-04-17 00:26:25: before update. Value: 
2024-04-17 00:26:26: in main. Value: main
2024-04-17 00:26:27: in update. Value: main
2024-04-17 00:26:27: after update. Value: update

到此这篇关于Go中sync.Mutex 加锁失效的问题解决的文章就介绍到这了,更多相关Go sync.Mutex 加锁失效内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

您可能感兴趣的文章:
阅读全文