首页 > 解决方案 > 使用等待组的 Golang 工作者

问题描述

我是 Golang 的新手,并试图了解 Golang 中的 WaitGroups 和并发是如何工作的。在此示例中,创建了 5 个工作人员,并使用一个通道将工作传递给每个工作人员。工作人员被要求休眠 1 秒以模拟繁重的计算。一切顺利,但程序并没有优雅地退出。而是打印此错误消息。请帮助理解为什么会发生这种情况。

fatal error: all goroutines are asleep - deadlock!

这是代码,


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

func worker(wg *sync.WaitGroup, messageChannel <-chan string) {
    defer wg.Done()
    for i := range messageChannel {
        time.Sleep(time.Second)
        fmt.Println("done processing - ", i)
    }
}

func stop(wg *sync.WaitGroup) {
    fmt.Println("waiting on the main thread")
    wg.Wait()
}

func main() {
    wg := new(sync.WaitGroup)
    messageChannel := make(chan string, 50)

    // create workers
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go worker(wg, messageChannel)
    }

    // input some messages
    for i := 0; i < 10; i++ {
        messageChannel <- fmt.Sprint(i)
    }

    stop(wg)
    close(messageChannel)
}

提前致谢!

标签: goconcurrencyworker

解决方案


为了扩展@Peter 的评论,这里是您编写的代码的执行流程:

  • 初始化后,你启动你的workergoroutines。每个workergoroutine 都会超过messageChannel,延迟 1 秒会打印出一条消息。
  • 接下来,您messageChannel通过 for 循环插入一些消息。每个可用worker的 goroutine 都会收到一条消息,直到所有消息都被处理并打印出来。之后,workergoroutines 正在等待来自messageChannel.
  • 在用于在中插入消息的 for 循环messageChannel完成后,您执行该stop函数,该函数阻塞wg.Wait()并等待所有wg.Done()调用在所有workergoroutine 中执行。然而,由于messageChannel没有关闭,没有一个workergoroutine 可以完成执行,也没有一个wg.Done()调用被执行。
  • workergoroutines 被卡住是因为它messageChannel永远不会关闭,maingoroutines 因为函数wg.Wait()内部的调用而被卡住stop,你最终会陷入一个所有 goroutines 都处于休眠状态的死锁。

根据建议,您只需要交换地点stopclose电话

//rest of the code
close(messageChannel)
stop(wg)

这样,当所有消息都插入到 中时messageChannel,您调用close(messageChannel)然后stop(wg),这会阻塞wg.Wait调用。该close(messageChannel)调用确保,一旦从 中读取所有消息messageChannel,goroutine内部的for-range循环将退出,并且所有调用都将被执行。一旦发生这种情况,将解除阻塞,程序将正确完成执行。messageChannelworkerdefer wg.Done()wg.Wait()


推荐阅读