【问题标题】:multiple sync.WaitGroup usage多个 sync.WaitGroup 使用
【发布时间】:2018-12-15 10:48:23
【问题描述】:

我见过sync.WaitGroup的几个不同的例子

示例 1

var wg sync.WaitGroup

wg.Add(1)
go doStuff(&wg)
wg.Wait()

示例 2

wg := new(sync.WaitGroup)

wg.Add(1)
go doStuff(wg)
wg.Wait()

区别在于sync.WaitGroup 的初始化方式 varnew

如果使用var 选项,它必须作为指针&wg 传递给goroutine,但如果我使用new 选项,我可以将其发送为wg

这两个例子有什么区别?以上2个哪个是正确的?在某些情况下,一个优先于另一个?

我正在编写一个创建多个sync.WaitGroups 的程序,那么使用newvar 是否重要?

【问题讨论】:

标签: pointers go concurrency


【解决方案1】:

您的两个示例都可以正常工作。另请注意,除了new(),您还可以使用composite literal 并像这样获取其地址:

var wg = &sync.WaitGroup{}

sync.WaitGroup 的方法有指针接收器,所以无论何时调用它的方法,都需要WaitGroup 结构值的地址。这不是问题,因为当wg 是非指针时,wg.Add(1)wg.Done() 调用是(&wg).Add(1)(&wg).Done() 的简写,因此编译器会自动“重写”这些调用以获取地址wg 首先,然后将该地址用作方法的接收者。

但是,我仍然认为,如果一个值仅用作指针(sync.WaitGroup 是一个光辉的例子),您应该首先声明它并将其用作指针,这样就可以减少空间错误。

例如,如果你使用一个非指针并且你声明函数期望一个非指针,并且你将它作为一个非指针传递,你不会得到编译时错误,但它会行为不端(@ 987654336@不应复制)。

虽然今天的 linter 会给你一个警告信息,但我相信最好始终使用指针。

使用指针的另一个原因:如果一个函数将返回一个 sync.WaitGroup,或者如果您有一个将 sync.WaitGroup 存储为值的映射,您将无法在结果上调用方法,因为返回值函数和映射索引操作是不可寻址的。如果函数将返回一个指针值,或者如果您首先将指针存储在映射中,您仍然可以调用这些方法,而不必将它们存储在局部变量中。详情见How can I store reference to the result of an operation in Go?

例如:

func getWg() sync.WaitGroup { return sync.WaitGroup{} }

getWg().Wait() // Compile-time error!

m := map[int]sync.WaitGroup{
    1: sync.WaitGroup{},
}

m[1].Wait() // Again: compile-time error

但是这些工作:

func getWg() *sync.WaitGroup { return &sync.WaitGroup{} }

getWg().Wait() // Works, you can call methods on the return value

m := map[int]*sync.WaitGroup{
    1: &sync.WaitGroup{},
}

m[1].Wait() // Also works

在此处了解更多信息:Why should constructor of Go return address?

【讨论】:

  • 谢谢,这有助于理解两者都是正确的。由于new / 复合文字技术创建了一个指针。您是否建议使用示例 2 样式,因为 var 是非指针?
  • @rrag 是的,我建议使用示例 2,并且使用 &sync.WaitGroup{} 比使用 new(sync.WaitGroup) 更常见,尽管它们的作用相同。
猜你喜欢
  • 2021-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-20
  • 1970-01-01
  • 2013-10-13
相关资源
最近更新 更多