首页 > 解决方案 > 持有锁直到所有 goroutine 完成

问题描述

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

  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 的副本。

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

标签: godistributed-systemconsensus

解决方案


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

  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()

如果不了解您要完成的工作,我不确定这是最好的解决方案。


推荐阅读