发生这种情况可能有多种原因,例如:
- 一个goroutine正在等待一个通道接收数据,但没有其他 goroutine 向该通道发送数据。
- 一个goroutine正在等待获取锁,但另一个 goroutine 正在持有锁。
- 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 和共享资源。