【问题标题】:Is it thread safe to create a new Mutex in Go?在 Go 中创建新的 Mutex 是线程安全的吗?
【发布时间】:2020-10-08 15:11:53
【问题描述】:

我在 Go 中有一个包含互斥锁的结构,我想确保该互斥锁永远不会是 nil。为此,我实现了一个GetMutex() 函数,它检查互斥锁是否为nil,如果是,则为其分配一个值。

我的问题是:以下代码线程安全吗?如果不是,那么确保mux 始终被初始化的惯用方法是什么?我唯一能想到的就是在我的GetMutex() 函数中使用这个包中的全局互斥锁,但也许有不同的方法。

package main

import (
    "sync"
)

type Counter struct {
    mux *sync.Mutex
    counter int
}

// Is this thread safe?
func (c *Counter) GetMux() *sync.Mutex {
    if c.mux == nil {
        c.mux = &sync.Mutex{}
    }
    return c.mux
}

func (c *Counter) Inc() {
    c.GetMux().Lock()
    c.counter++
    c.GetMux().Unlock()
}

func main() {
    c := &Counter{}
    c.Inc()
}

【问题讨论】:

    标签: go struct concurrency mutex


    【解决方案1】:

    不,如果同时从多个 goroutine 调用 Counter.GetMux() 是不安全的:GetMux() 同时读取和写入 Counter.mux 字段。

    一般的方法是使用类似“构造函数”的函数来处理初始化,如下所示:

    func NewCounter() *Counter {
        return &Counter{
            mux: &sync.Mutex{},
        }
    }
    

    当然总是用这个NewCounter()创建计数器。

    另一种有限的方法是使用非指针互斥值:

    type Counter struct {
        mux     sync.Mutex
        counter int
    }
    

    所以当你有一个Counter 结构值时,它——按照设计——包括一个互斥体。但是如果你这样做,那么 Counter 应该始终用作指针,并且不能复制 Counter 结构值(否则也将复制互斥体字段,但正如 sync 的包文档所述:“不应复制包含此包中定义的类型的值。”)。

    这样做的明显优势是Counterzero value 是一个有效且准备就绪的计数器(您应该针对您的自定义类型进行定位),并且不需要构造函数。

    【讨论】:

      猜你喜欢
      • 2013-11-07
      • 1970-01-01
      • 2010-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多