为什么会有这个问题?
突然看到函数传参的问题,就想知道直接传参,Mutex是传值还是传引用
结论
值传递
Mutex定义
type Mutex struct {
state int32
sema uint32
}
看下定义就知道了,结构体,而且里面的字段类型都是普通值类型,那函数传参如果不特定使用指针,那就是值传递
抛出一个问题
下面代码会报panic
func TestMutex1(t *testing.T) {
mux := sync.Mutex{}
mux.Lock()
mux.Unlock()
mux.Unlock() //fatal error: sync: unlock of unlocked mutex
}
而这段代码不会:
func selfUnlock(mux sync.Mutex) {
fmt.Printf("before selfUnlock: %p, %v\n", &mux, mux)
mux.Unlock()
fmt.Printf("after selfUnlock: %p, %v\n\n", &mux, mux)
}
func TestMutex2(t *testing.T) {
mux := sync.Mutex{}
fmt.Printf("init mutex: %p, %v\n", &mux, mux)
mux.Lock()
fmt.Printf("after lock: %p, %v\n\n", &mux, mux)
selfUnlock(mux)
selfUnlock(mux)
}
表面上看都是对同一个互斥锁进行了两次的解锁操作,但是后者没有发生panic,为什么?
执行一下TestMutex2
就能看出结果:
=== RUN TestMutex2
init mutex: 0xc0000162a8, {0 0}
after lock: 0xc0000162a8, {1 0}
before selfUnlock: 0xc0000162c0, {1 0}
after selfUnlock: 0xc0000162c0, {0 0}
before selfUnlock: 0xc0000162d8, {1 0}
after selfUnlock: 0xc0000162d8, {0 0}
--- PASS: TestMutex2 (0.00s)
首先三处的地址不一样,这是自然的,毕竟函数传参了,然后就是具体的值,发现两次在调用 unlock() 函数的时候传入的值都是 Mutex{1, 0},也就是说传进函数 selfUnlock 的锁都是处于 lock 的状态,这也就是为啥一个锁可以 unlock 两次而没有panic,实际上是因为值传递,已经是两个锁了
结论
Mutex 的传递是值传递
函数传参会发生值拷贝(也就是复制了锁的状态),所以一定注意在不同函数内操作同一个锁时需要主动使用指针进行传递,如下:
func selfUnlock3(mux *sync.Mutex) {
fmt.Printf("before selfUnlock: %p, %v\n", mux, *mux)
mux.Unlock()
fmt.Printf("after selfUnlock: %p, %v\n\n", mux, *mux)
}
func TestMutex3(t *testing.T) {
mux := sync.Mutex{}
fmt.Printf("init mutex: %p, %v\n", &mux, mux)
mux.Lock()
fmt.Printf("after lock: %p, %v\n\n", &mux, mux)
selfUnlock3(&mux)
fmt.Printf("main after selfUnlock: %p, %v\n", &mux, mux)
}
指针传递,达到了在func内对Mutex解锁的目的!
你好~