首页  >  文章  >  后端开发  >  在 Go 中使用 WaitGroups 和缓冲通道时如何防止死锁?

在 Go 中使用 WaitGroups 和缓冲通道时如何防止死锁?

Linda Hamilton
Linda Hamilton原创
2024-10-26 18:10:02299浏览

How can I prevent deadlock when using WaitGroups and buffered channels in Go?

Go 中的死锁:WaitGroup 和缓冲通道

在 Go 中,当并发 goroutine 无限期地等待彼此完成时,就会发生死锁。死锁的一个常见原因涉及使用 WaitGroups 和缓冲通道。

死锁示例

考虑以下代码:

<code class="go">package main

import "fmt"
import "sync"

func main() {
    ch := make(chan []int, 4)
    var m []int

    var wg sync.WaitGroup
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            ch <- m // Sending to a full channel
            return
        }()
    }
    wg.Wait()

    for c := range ch {
        fmt.Printf("c is %v", c)
    }
}</code>

此代码打算将 5 个空切片发送到容量为 4 的缓冲通道,然后在所有 goroutine 完成后从通道中读取。但是,该代码会导致死锁错误。

死锁的原因

死锁是由于两个问题引起的:

  1. 通道缓冲区不足:通道容量为 4,对于 5 个尝试发送数据的 goroutine 来说太小了。当通道变满时,后续等待发送数据的 goroutine(第 15 行)将无限期地阻塞。
  2. 阻塞通道迭代: 迭代通道的循环(第 22-24 行)会阻塞无限期地,因为它等待更多元素到达通道。由于所有 goroutine 都已完成发送数据,并且预计不再有数据,因此如果没有相应的 goroutine 从通道读取数据,本次迭代将永远不会完成。

解决方案

要解决死锁,请进行以下修改之一:

解决方案 1:

将通道容量增加到 5(或更多)并在发送完所有数据后将其关闭:

<code class="go">ch := make(chan []int, 5)
...
wg.Wait()
close(ch)</code>

解决方案2:

启动一个单独的goroutine从通道读取数据,并在接收到所有数据后通知主goroutine:

<code class="go">func main() {
    ch := make(chan []int, 4)
    var m []int

    var wg sync.WaitGroup
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func() {
            ch <- m
            wg.Done()
        }()
    }
    go func() {
        for c := range ch {
            fmt.Printf("c is %v\n", c)
            wg.Done()
        }
    }()
    wg.Wait()
}</code>

以上是在 Go 中使用 WaitGroups 和缓冲通道时如何防止死锁?的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn