golang中锁的值类型和引用类型区别

在对象中引入Mutex来保证原子性,但是当对象是值引用的时候,发现索没有效果。它内部的实现逻辑到底是怎么样的?

代码如下:

package main

import (
    "fmt"
    "sync"
)

//这里声明了对象
type people struct {
    sync.Mutex
}

//它的值类型的方法
func (p people) test(str string) {
    p.Lock();
    defer p.Unlock();

    for i := 0; i < 5; {
        fmt.Println(str, i)
        i++
    }
}

func main() {
    var user people
    var wg sync.WaitGroup

    wg.Add(2)
    go func() {
        defer wg.Done()
        user.test("go1")
    }()

    go func() {
        defer wg.Done()
        user.test("go2")
    }()

    wg.Wait()
}

这样执行的话,看不出效果来,然后加上这样一行代码:

func (p people) test(str string) {
    p.Lock();
    defer p.Unlock();

    for i := 0; i < 5; {
        fmt.Println(str, i)
        
        //加的代码在这里!这里!
        time.Sleep(time.Second)
        i++
    }
}

发现执行的结果如下:

go2 0
go1 0
go2 1
go1 1
go1 2
go2 2
go2 3
go1 3
go1 4
go2 4

疑惑点:在整个逻辑过程中,只有一个变量user,没有发生值拷贝的过程,怎么好端端的锁就不生效了呢

阅读 3.4k
3 个回答

首先mutex不能被拷贝,一旦mutex被拷贝,加锁及解锁就作用在不同的mutex对象上,自然就失去了效果.

另外对于方法的接收对象,接收者是值和指针是有本质区别的:
1.当接收者是指针时进行函数调用时传递的是该对象的指针.
2.当接收者是值时进行函数调用传递的就是值拷贝.
这里可以通过在函数调用中对对象的值进行更改进行验证.

因此,people对象进行函数调用时,people已经拷贝了新值,生成了新的mutex,所以锁就失效了.这里将test方法接收者改成*people即可.

func (p *people) test(str string) {
    // balabala
}

写代码尝试了一下:确实如阁下说的一样。所以我总结一下:

  1. 针对值调用的方法,在方法体内部会复制对象的一份副本,该副本无法保存,在方法调用结束后被释放。所以,要想让方法体内的对象保存新的修改,需要使用引用调用。
  2. 下面代码中,只对象声明的user,不能理解为对象,而应该看做是一种调用的形式。当然只是针对值声明的类型函数。
func (p people) getAge() int  {
    return p.age
}

func (p people) setAge(newAge int)  {
    p.age = newAge
    fmt.Println("copy a object. age ", p.age)
}

func main() {
    var user people
    user.setAge(1)
    fmt.Println(user.getAge())
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题