go - 对 Go 中的 Locks/Mutex 感到困惑

标签 go locking mutex

我正在尝试构建 map 。通常所有读取都可以并行完成,除非写入时,所有读取都需要锁定。我以为我了解 Mutex 在 go 中的工作原理,但显然我不了解。

我首先尝试使用 RWMutex 写锁:

type person struct {
    sync.RWMutex
    age int
}

func main() {
    a := person{age: 3}
    fmt.Println(a.age)
    go func() {
        a.Lock()
        time.Sleep(5 * time.Second)
        a.age = 4
        fmt.Println(a.age)
        a.Unlock()
    }()
    fmt.Println(a.age)
    fmt.Println("main", a.age)
    time.Sleep(20 * time.Second)
}

我有点期待写锁会把读操作锁上a.age。相反,我得到了:

3
3
main 3
4

所以我决定也添加一个读锁:

func main() {
    a := person{age: 3}
    fmt.Println(a.age)
    go func() {
        a.Lock()
        a.RLock()
        time.Sleep(5 * time.Second)
        a.age = 4
        fmt.Println(a.age)
        a.Unlock()
        a.RUnlock()
    }()
    fmt.Println(a.age)
    fmt.Println("main", a.age)
    time.Sleep(20 * time.Second)
}

更糟糕的是,我得到了:

3
3
main 3

显然我不明白这是如何工作的。感谢您的帮助。

最佳答案

切勿双锁。您的问题是您没有将 main 末尾的读取包装在锁中 - 如果他们不尝试建立锁,则没有什么可以阻止他们在其他东西写入时读取(甚至如果写入正在使用锁)。锁本身就是提供互斥 (MutEx) 的东西,因此只有持续使用它才能获得它:

func main() {
    a := person{age: 3}
    fmt.Println(a.age)
    go func() {
        a.Lock()
        time.Sleep(5 * time.Second)
        a.age = 4
        fmt.Println(a.age)
        a.Unlock()
    }()
    a.RLock()
    fmt.Println(a.age)
    fmt.Println("main", a.age)
    a.RUnlock()
    time.Sleep(20 * time.Second)
}

这里没有魔法发生;实际上是调用 LockRLock 来进行锁定。如果您不调用它们,则不会阻止并发访问。当您调用 Lock 时,它会一直等到它可以完全锁定自己,然后锁定它并返回。当您调用 RLock 时,它会等待直到没有写锁,然后获取一个(共享的)读锁。它正在调用那些提供互斥的函数,而不是幕后发生的任何魔法。

关于go - 对 Go 中的 Locks/Mutex 感到困惑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49201112/

相关文章:

go - 锁定文件以供阅读

sqlite数据库连接/锁定问题

c++ - 你能帮我将互斥对象应用到这段代码吗?

c - 等到上一个进程实例完成

go - 导入本地文件

sqlite - 如何从配置文件获取路径

go - 使用 golang Viper lib 进行高级配置

c++ - 加锁前检查Thread是否已经加锁mutex

VB.NET 让消息框在应用程序退出后继续存在

multithreading - timed_mutex 不会在 Cygwin 4.8.2 ('timed_mutex' 下编译,命名空间 'std' 没有命名类型)