【问题标题】:Use mutex or mutex pointer in Golang?在 Golang 中使用互斥锁或互斥指针?
【发布时间】:2021-08-19 12:16:40
【问题描述】:

我有一个代码sn-p:

type calculation struct{
    sum int
    mutex sync.Mutex
}

func dosomething(c *calculation , wg *sync.WaitGroup) {
    c.mutex.Lock()
    c.sum++
    c.mutex.Unlock()
    wg.Done()
}

func main() {
    t := time.Now()
    c := new(calculation)
    wg := new(sync.WaitGroup)
    for i:=0; i<10000000; i++{
        wg.Add(1)
        go dosomething(c, wg)
    }
    wg.Wait()
    fmt.Println(c.sum)
    fmt.Println(time.Since(t))
}

但我发现使用互斥指针也可以:

type calculation struct{
    sum int
    mutex *sync.Mutex
}

func dosomething(c *calculation , wg *sync.WaitGroup) {
    c.mutex.Lock()
    c.sum++
    c.mutex.Unlock()
    wg.Done()
}

func main() {
    t := time.Now()
    c := &calculation{0, new(sync.Mutex)}
    wg := new(sync.WaitGroup)
    for i:=0; i<10000000; i++{
        wg.Add(1)
        go dosomething(c, wg)
    }
    wg.Wait()
    fmt.Println(c.sum)
    fmt.Println(time.Since(t))
}

我对这两个版本做了一些测试,发现经过的时间很接近。

那么我应该使用哪一个?为什么我应该使用互斥体或互斥体指针?它们有性能差异吗?

【问题讨论】:

  • 好吧,根据sync docs,“不应复制包含此包中定义的类型的[v]值。”。但是你不是在这里复制它,因为c 是一个指针。

标签: go pointers mutex


【解决方案1】:

Go sync.Mutex 不能被复制,所以当你有一个可以被合理复制的数据结构但它本身的某些部分是shared 并且不能被合理地复制,并且您计划通过值传递它和/或返回它,以便它被复制。

例如:

type Thingy struct {
    notShared int
    mtx *Sync.mutex
    shared *int
}

func (t Thingy) Thingy {
    t.notShared += 3
    t.mtx.Lock()
    *t.shared *= 2
    t.mtx.Unlock()
    return t
}

(可能更典型的是有多个非共享字段,然后一个指向struct 的指针,其中包含互斥体本身和共享字段。以上主要用于说明)

鉴于您的 calculation 结构本身始终作为指针传递,但没有理由为您的示例添加间接级别。

编辑:您添加了一个关于性能差异的问题。添加额外的(否则不必要的)间接通常会降低性能,但一般来说,您应该首先编写可读性最高的代码,然后针对性能问题对其进行测量。在我看来,添加额外的不必要的间接会使代码的可读性稍差一些,因此在这里,性能和可读性是相辅相成的。

【讨论】:

  • 谢谢你。 “编写最易读的代码”对我帮助很大。此处的互斥锁仅由 struct 的实例 lock/unlock 使用。显然我不希望其他人修改锁定状态。所以这里的互斥指针(共享)是没有意义的。但是我认为这里的互斥指针也是合法的,主要区别可能是存储互斥量字面量+指向互斥量的指针或存储互斥量指针(经过几天搜索go中struct的内存结构后不确定),所以我认为这里没有这种“额外的间接成本”,还需要更多的努力去探索。
  • @Artisan:Go 是一种非常简单的语言。如果你声明某事为*T,对于某些类型T,Go 将使用指向T 的指针。这意味着对对象的访问需要通过指针进行间接访问。 Go 有时能够优化掉一些间接性,但总的来说这会增加工作量。跟踪指针的成本通常很小,但首先使用指针的成本可能会更大,因为这意味着垃圾收集器需要做更多的工作。
  • 一般来说,如果满足以下条件,则值得支付此成本:(a) 我们必须拥有指针,因为我们需要修改(共享)对象,或者 (b) 我们获得在其他地方节省了一大笔钱。否则,这只是一个成本:也许很小,但最终它可能会增加一些东西。
  • 哦!我应该把类型 struct T 作为一个整体来对待,如果类型 T 被 *T 访问,那么 T.mtx 值也会被找到。如果 T 的 mutex 字段被声明为 *mutex,则会产生额外的成本。谢谢!
猜你喜欢
  • 1970-01-01
  • 2018-05-23
  • 1970-01-01
  • 2019-04-04
  • 2011-01-12
  • 2012-06-05
  • 2010-09-16
  • 2010-12-17
  • 1970-01-01
相关资源
最近更新 更多