持有锁直到所有 goroutines 完成

hold a lock until all goroutines finishes

下面是一段用 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.

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

One way to fix this is for the created goroutine to use a copy of rf.currentTerm made while the outer code holds the lock.

但我不是很清楚它是什么意思。

您可以将参数传递给 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()

在不了解您要完成的目标的情况下,我不确定这是最佳解决方案。