保持锁定直到所有 goroutine 完成

问题描述

下面是一段用 Go 编写的代码,取自 Raft Locking Advice,规则 5:

  rf.mu.Lock()
  rf.currentTerm += 1
  rf.state = Candidate
  for <each peer> {
    go func() {
      rf.mu.Lock()
      args.Term = rf.currentTerm
      rf.mu.Unlock()
      Call("Raft.RequestVote",&args,...)
      // handle the reply...
    } ()
  }
  rf.mu.Unlock()

Raft 是某种共识算法,rf 指的是 Raft。上面的代码试图做的是,领导者正在向其所有对等方发送 RPC 请求,要求投票。正如代码后面的段落所指出的,这段代码并不理想,因为 rf.currentTerm 可能在子例程被触发之前已经改变了。目标是让 rf 的状态在其同行投票时保持不变。

我的补救方法是使用等待组让代码在最后一个 rf.mu.Unlock 之前等待。那肯定会解决提到的问题。然而,建议中的规则 4 指出:it's usually a bad idea to hold a lock while doing anything that might wait.

文章提出的解决方案如下:

解决这个问题的一种方法是让创建的 goroutine 使用外部代码持有锁时制作的 rf.currentTerm 副本。

但我不太确定这意味着什么。

解决方法

您可以将参数传递给 go 例程函数,例如:

  rf.mu.Lock()
  rf.currentTerm += 1
  rf.state = Candidate
  for <each peer> {
    go func(ct CurrentTerm) {
      rf.mu.Lock()
      args.Term = ct
      rf.mu.Unlock()
      Call("Raft.RequestVote",&args,...)
      // handle the reply...
    } (rt.currentTerm)
  }
  rf.mu.Unlock()

如果不了解您要实现的目标,我不确定这是最佳解决方案。