首页 > 后端开发 > Golang > 正文

为什么提供的带有 WaitGroup 和缓冲通道的 Go 代码会导致死锁?

Barbara Streisand
发布: 2024-10-26 22:08:02
原创
680 人浏览过

Why does the provided Go code with WaitGroup and buffered channel result in a deadlock?

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
            return
        }()
    }
    wg.Wait()

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

此代码预计会创建一个缓冲区大小为 4 的通道并启动 5 个 goroutine ,每个都向通道发送一个空切片。主协程等待所有协程完成,然后在通道上进行范围。

但是,此代码将导致死锁。为什么?

死锁原因:

代码中存在两个问题:

  1. 通道容量:通道的容量为4,这意味着它最多可以容纳 4 个元素。然而,有 5 个 Goroutine 试图发送到 Channel,导致最后一个 Goroutine 会阻塞等待一个 slot 被释放。
  2. 关闭 Channel:range ch 循环继续等待元素进入频道。由于没有更多的 goroutine 可以写入通道,循环将无限期地等待。

解决方案:

  1. 增加通道容量:通过将通道容量增加到 5,将有足够的槽位供所有 goroutine 发送它们的值而不会阻塞。此外,在 goroutine 完成写入后关闭通道将向范围循环发出信号,表明不再有元素到来,从而防止它无限期地等待。

    <code class="go">ch := make(chan []int, 5)
    ...
    wg.Wait()
    close(ch)</code>
    登录后复制
  2. 使用 Done Loop 中的 (): 可以使用 WaitGroup 的 Done() 方法在最后一个 goroutine 完成时发出信号,而不是关闭通道。通过在 range 循环中调用 Done(),当通道为空并且循环可以退出时,主 goroutine 将收到通知。

    <code class="go">go func() {
        for c := range ch {
            fmt.Printf("c is %v\n", c)
            wg.Done()
        }
    }()
    wg.Wait()</code>
    登录后复制

这些解决方案通过确保以下方式解决了死锁:通道有足够的容量,并且当没有更多元素可以从通道读取时,范围循环退出。

以上是为什么提供的带有 WaitGroup 和缓冲通道的 Go 代码会导致死锁?的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:php.cn
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责声明 Sitemap
PHP中文网:公益在线PHP培训,帮助PHP学习者快速成长!