【问题标题】:Can't handle a go routine deadloock无法处理 goroutine 死锁
【发布时间】:2019-12-26 18:53:03
【问题描述】:

我正在尝试向频道广播一条消息,我只希望它发送 5 条消息。但我总是得到这个错误: 致命错误:所有 goroutine 都处于休眠状态 - 死锁!

我的代码:

package main

import (
    "log"
    "sync"

    broadcast "github.com/dustin/go-broadcast"
    "github.com/pwaller/barrier"
)

//Message boradcasted
type Message struct {
    y string
    x int
}

var w sync.WaitGroup
var bar barrier.Barrier

func main() {

    b := broadcast.NewBroadcaster(100)

    w.Add(1)
    go workerOne(b)

    d := Message{"message :", 0}

    go func() {
        for i := 0; i < 5; i++ {
            d.x = i
            log.Printf("Sending %v", d)
            b.Submit(d)
        }
        <-bar.Barrier()
        b.Close()
    }()
    w.Wait()
}

func workerOne(b broadcast.Broadcaster) {
    ch := make(chan interface{})
    b.Register(ch)

    for {
        v, ok := <-ch
        if ok {
            log.Printf("workerOne() reading : %v", v)
        } else {
            log.Printf("i am here")
            close(ch)
            b.Unregister(ch)
            bar.Fall()
            w.Done()
            return
        }
    }
}

输出:

2019/12/26 20:34:11 Sending {message : 0}
2019/12/26 20:34:11 Sending {message : 1}
2019/12/26 20:34:11 Sending {message : 2}
2019/12/26 20:34:11 Sending {message : 3}
2019/12/26 20:34:11 Sending {message : 4}
2019/12/26 20:34:11 workerOne() reading : {message : 0}
2019/12/26 20:34:11 workerOne() reading : {message : 1}
2019/12/26 20:34:11 workerOne() reading : {message : 2}
2019/12/26 20:34:11 workerOne() reading : {message : 3}
2019/12/26 20:34:11 workerOne() reading : {message : 4}
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [semacquire]:
sync.runtime_Semacquire(0x59f530)
        C:/Go/src/runtime/sema.go:56 +0x40
sync.(*WaitGroup).Wait(0x59f528)
        C:/Go/src/sync/waitgroup.go:130 +0x6c
main.main()
        C:/Users/DELL/Desktop/work/demos/Design pattern/broadcast/broadcast.go:38 +0x107

goroutine 19 [select]:
github.com/dustin/go-broadcast.(*broadcaster).run(0xc000060420)
        D:/gocode/src/github.com/dustin/go-broadcast/broadcaster.go:39 +0x10f
created by github.com/dustin/go-broadcast.NewBroadcaster
        D:/gocode/src/github.com/dustin/go-broadcast/broadcaster.go:64 +0x103

goroutine 20 [chan receive]:
main.workerOne(0x4f1de0, 0xc000060420)
        C:/Users/DELL/Desktop/work/demos/Design pattern/broadcast/broadcast.go:46 +0xcd
created by main.main
        C:/Users/DELL/Desktop/work/demos/Design pattern/broadcast/broadcast.go:25 +0x8e

goroutine 21 [chan receive]:
main.main.func1(0xc000060440, 0x4f1de0, 0xc000060420)
        C:/Users/DELL/Desktop/work/demos/Design pattern/broadcast/broadcast.go:35 +0x168
created by main.main
        C:/Users/DELL/Desktop/work/demos/Design pattern/broadcast/broadcast.go:29 +0xf7
exit status 2

我尝试了所有方法,但它没有抛出条件!在 workerOne() 函数中关闭通道并结束等待,但仍然有相同的错误

【问题讨论】:

  • 你有一个工人和一个频道,但你有所有这些额外的东西——你想在这里做什么?你为什么使用broadcast?你为什么使用barrier(它在自述文件中说它已被弃用)?
  • 一般来说会发生死锁,因为所有的 goroutine 都在等待其他 goroutine,这意味着它们都无法继续。使用这些第三方库而不是内置语言结构使得更难判断究竟是什么导致了这种情况以及您要完成什么。
  • @Adrian 我正在学习广播,以便稍后在我的项目中使用它,所以我试图先理解它,当 b.Submit(d) 结束广播并在 workerOne() 之前关闭时,这是一个问题函数接收所有消息,所以我使用屏障阻止它在 workerOne() 函数获取所有消息之前关闭
  • 我不建议使用已弃用的库。看广播,我也不能说我会推荐它;为了节省一两打代码,它牺牲了类型安全性,这是一个相当大的代价而付出的代价很小。

标签: go broadcast goroutine


【解决方案1】:

作为Adrian noted in a comment,您使用的barrier 包已弃用,取而代之的是Go 包含的context 包。您应该改用context。 (目前,您使用的 broadcast 软件包对您也没有任何好处。)

不过,眼前的问题似乎很明显:运行workerOne 的(单个)goroutine 从一个通道读取,并且只有当通道关闭时(这样ok 才会变成false ) 是否调用bar.Fall() 来放下障碍。同时,运行匿名发送者函数的(单个)goroutine:

go func() {
    for i := 0; i < 5; i++ {
        d.x = i
        log.Printf("Sending %v", d)
        b.Submit(d)
    }
    <-bar.Barrier()
    b.Close()
}()

通过broadcast 包的(非常)简单的消息发布/订阅接口提交五个项目,然后等待障碍下降。唯一会丢弃障碍的 goroutine(将其视为唯一可以在这里救你的 gopher)是运行 workerOne 的 goroutine。他现在正在频道接收中等待:

    v, ok := <-ch

您,在您的主 goroutine 中,正在通过您的 sync.WaitGroup 变量等待:

w.Wait()

运行匿名发送函数的 gopher 正在等待:

    <-bar.Barrier()

谁——这三个 gopher 中的哪一个在各种 Go 例程中运行或等待——将向匿名发送者中的 gopher 发出信号,v, ok := &lt;-ch 应该得到!ok 结果?唯一可以的是运行workerOne的地鼠,但他被卡住了。

broadcast 包永远不会为您关闭您的频道。取消注册频道只是将其从广播公司的输出频道中删除,这会使未注册的频道保持打开状态;并且完全有效地关闭广播公司只是取消注册所有频道,再次让它们保持打开状态。因此,如果您希望关闭您的频道,则必须自己在其他地方进行。

或者,您可以修改您的workerOne,使其不依赖于关闭的频道。例如,您的变量d 有一个x int 和一个y string;您可以选择其中一个或两个,并确定一个字符串值“放下障碍”——也许还有一些特定的int 值——意味着“现在调用bar.Fall()”。但是,如果您这样做,您从这两个非标准软件包中获得的收益会更少。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-22
    • 2011-07-15
    • 2012-09-06
    • 2019-09-07
    相关资源
    最近更新 更多