发生这种情况可能有多种原因,例如:

  1. 一个goroutine正在等待一个通道接收数据,但没有其他 goroutine 向该通道发送数据。
  2. 一个goroutine正在等待获取锁,但另一个 goroutine 正在持有锁。
  3. goroutine正在等待满足条件,但条件永远不会满足。

当两个或多个goroutines 正在等待彼此完成,并且它们都无法继续时,就会发生死锁。使用频道和同步时可能会发生这种情况。互斥同步。导致循环依赖关系的等待组。

下面是死锁情况的示例。

package main

import (
  "fmt"
  "sync"
  "time"
)

func main() {
  wg := new(sync.WaitGroup)
  messages := make(chan string)
  
  for x := 1; x <= 4; x++ {
    wg.Add(1)
    go messageReceiever(x, wg, &messages)
  }

  for msg := range messages {
    fmt.Println(msg)
  }
  wg.Wait()
  close(messages)
}

func messageReceiever(count int, wg *sync.WaitGroup, messages *chan string) {
  defer wg.Done()
  time.Sleep(time.Millisecond * time.Duration(1000))
  *messages <- fmt.Sprintf("John Wick: %d", count)
}

输出

John Wick: 1
John Wick: 4
John Wick: 2
John Wick: 3
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:

上面代码中的问题是我们使用了一个带有范围的 for 循环从消息通道读取,但直到循环之后我们才关闭通道。这会导致循环无限期地等待更多消息,从而造成死锁

要修复致命错误您可以移动wg.Wait()在for循环之前,并在所有其他 goroutines 完成后使用单独的goroutine关闭消息通道。

下面是修复后的示例代码:

package main

import (
  "fmt"
  "sync"
  "time"
)

func main() {
  wg := new(sync.WaitGroup)
  messages := make(chan string)

  // 启动goroutines
  for x := 1; x <= 4; x++ {
    wg.Add(1)
    go messageReceiever(x, wg, messages)
  }

  // 单独启动一个goroutine等待所有goroutine结束并关闭channel
  go func() {
    wg.Wait()
    close(messages)
  }()

  // 从channel读取
  for msg := range messages {
    fmt.Println(msg)
  }
}

func messageReceiever(count int, wg *sync.WaitGroup, messages chan string) {
  defer wg.Done()
  time.Sleep(time.Millisecond * time.Duration(1000))
  messages <- fmt.Sprintf("John Wick: %d", count)
} 

输出

John Wick: 3
John Wick: 4
John Wick: 1
John Wick: 2

在这个固定版本中,我们创建了一个新的goroutine,等待其他goroutines完成使用wg.Wait(),然后关闭消息通道。一旦消息关闭,带有范围的 for 循环现在将退出,从而避免死锁。

 

以下是避免 Go 中死锁的一些提示:

使用通道在 goroutines 之间进行通信。

使用锁保护共享资源。

使用条件等待事件发生。

使用同步包来帮助您管理 goroutines 和共享资源。