Go中并发数据收集的最佳方式是什么?为什么?

Go中并发数据收集的最佳方式是什么?为什么?,go,concurrency,goroutine,Go,Concurrency,Goroutine,我在实践中发现了两种收集数据的方法。我的同事使用不同的方法。但是,github上的大多数项目(例如vault)都使用第二种方式。我不能决定哪一个更好。你能解释一下为什么这样或那样比另一种好吗 代码: 主程序包 进口( “fmt” “同步” ) //复杂请求 func getData()字符串{ 返回“测试” } func main(){ //第一条路 var wg1 sync.WaitGroup var mu1 sync.Mutex res1:=make([]字符串,0,10) 对于i:=0;i

我在实践中发现了两种收集数据的方法。我的同事使用不同的方法。但是,github上的大多数项目(例如vault)都使用第二种方式。我不能决定哪一个更好。你能解释一下为什么这样或那样比另一种好吗

代码:

主程序包
进口(
“fmt”
“同步”
)
//复杂请求
func getData()字符串{
返回“测试”
}
func main(){
//第一条路
var wg1 sync.WaitGroup
var mu1 sync.Mutex
res1:=make([]字符串,0,10)
对于i:=0;i<10;i++{
wg1.添加(1)
go func(wg*sync.WaitGroup,mu*sync.Mutex){
推迟工作组完成()
木锁()
res1=append(res1,getData())
mu.Unlock()
}(&wg1和&mu1)
}
wg1.Wait()
fmt.Println(res1)
//第二条路
var wg2 sync.WaitGroup
ch:=制造(成串,10)
对于i:=0;i<10;i++{
wg2.添加(1)
go func(wg*sync.WaitGroup,ch chan字符串){
推迟工作组完成()

你问题的问题是,“更好”一词的使用需要一套标准来进行比较。在日常生活中,这套标准通常是隐含的,并且是沟通的人共享的,因此单独使用“更好”一词是可以的。(但即使如此,当你听到,比如,“这辆车比那辆车好”——你确定谁说你在车上共享相同的单词视图吗?还是让我们来考虑“Python胜过Go”这句话,它意味着“对什么任务更合适?” 在工程中,问“什么更好”需要严格定义提问者心中的标准,而你的问题没有;基本上这就是为什么它被否决的原因

如果我们考虑你的两个例子,它们大多是相同的,差异可以概括为:

  • 第一个更新生成的数据结构,通过显式锁保护操作
  • 第二种方法使用隐式锁在中间数据结构中缓冲数据,一旦收集到数据,就将其复制到结果数据结构中
再说一遍,哪一个更好

如果考虑可读性和维护成本,前者具有较少的行计数,但它有一个明确的锁操作,有时是不赞成的。 后者具有更大的行数,并且假定具有更大的WTF因子(因为读者可能会对看似不必要的缓冲的使用感到困惑,请参见下文)

如果我们考虑表现,我会推测前者的方法会赢(小幅度)。 原因是,从多个goroutine访问通道确实使用了同步,尽管它隐藏在编译的运行时代码中,以代替那些
操作,而且我不希望通道上的争用比互斥上的争用花费更少的CPU周期。
然后考虑在收集完所有的GORDUTIN之后,在第二种情况下,将数据放在一个缓冲通道中,然后将其铲进得到的切片(一步一步)。内存拷贝与通道访问混合在一起,在无争用的情况下会很快,但每个通道访问必须确保没有竞争者,并且在读取操作期间“锁定”然后“解锁”通道的内部结构,并且在每个步骤上更新通道的长度

再说一次:如果我们讨论的是10条复制成本低的数据类型,那么性能差异将很小,我会选择读起来最好的数据。
但如果它真的是一个模型示例,并且在生产代码中将包含100k个数据单元,或者1e6个数据块,那么情况将有所不同

在一个旁注中,请考虑在第一个例子中调用<代码> GETDATA()/<代码>,而持有锁可能不通过代码审查。< /P>


TL;DR
当性能看起来不成问题时,更喜欢可读性而不是性能。
否则。
当倾向于可读性时


这是因为,如果在获取锁后调用了某些代码,则该锁可能会被保留;请考虑:

s.mu.Lock()
res=append(res,fooBar())
s、 mu.Unlock()
在这里,如果
fooBar()
崩溃,将不会调用
s.mu.Unlock()

显然,这可以通过将整个过程分解成一个helper函数来解决,并将互斥解锁语句延迟到那里。
更好的是,在这段代码中,可能最好将值拆分为
append
append
本身:因为
append
不能崩溃(除非它无法分配内存,但在一个Go程序中,这将是一个硬崩溃),所以只需将它包装在这样的锁+解锁组合中就安全了。
还有,有些人会认为这是不可能的,因为有人可能想在锁和解锁语句之间增加更多代码,然后我们回到正方形。