为了账号安全,请及时绑定邮箱和手机立即绑定

在监视器 goroutine 中递归发送

在监视器 goroutine 中递归发送

Go
30秒到达战场 2021-12-20 10:11:45
在我正在编写的一个简单的计时器调度程序中,我正在使用监视器 goroutine 来同步开始/停止和计时器完成事件。监控 goroutine,当精简到基本的时候,看起来像这样:actions := make(chan func(), 1024)// monitor goroutinego func() {    for a := range actions {        a()    }}()actions <- func() {    actions <- func() {        // causes deadlock when buffer size is reached    }}这很有效,直到发送一个动作来发送另一个动作。计划的操作可能会计划另一个操作,这会在达到缓冲区大小时导致死锁。有没有什么干净的方法来解决这个问题而不诉诸共享状态(我在我的特定问题中尝试过,但很丑陋)?
查看完整描述

1 回答

?
茅侃侃

TA贡献1842条经验 获得超21个赞

问题源于这样一个事实,即当您的监控 goroutine “取出”(接收)一个值(一个函数)并执行它(这也发生在监控 goroutine 上)时,它在执行过程中会在受监控的通道上发送一个值。


这本身不会导致死锁,因为函数执行时( a()),它已经从缓冲通道中取出,因此其中至少有一个空闲空间,因此在通道内部发送新值a()可以继续进行而不会阻塞.


如果还有其他 goroutines 也可能在受监控的通道上发送值,则可能会出现问题,这就是您的情况。


避免死锁的一种方法是,如果正在执行的函数试图“放回”(发送)一个不在同一个 goroutine(即监视器 goroutine)中的函数,而是在一个新的 goroutine 中,那么监视器 goroutine 不会被阻塞:


actions <- func() {

    // Send new func value in a new goroutine:

    // this will never block the monitor goroutine

    go func() {

        actions <- func() {

            // No deadlock.

        }

    }()

}

通过这样做,即使缓冲区actions已满,monitor goroutine 也不会被阻塞,因为在它上面发送一个值将发生在一个新的 goroutine 中(可能会被阻塞,直到缓冲区中有可用空间)。


如果你想避免在发送一个值时总是产生一个新的 goroutine actions,你可以使用 aselect来首先尝试发送它而不产生一个新的 goroutine。仅当缓冲区actions已满且无法在不阻塞的情况下发送时,您才应生成用于发送的 goroutine 以避免死锁,这取决于您的实际情况可能很少发生,在这种情况下无论如何避免死锁是不可避免的:


actions <- func() {

    newfv := func() { /* do something */ }

    // First try to send with select:

    select {

    case actions <- newfv:

        // Success!

    default:

        // Buffer is full, must do it in new goroutine:

        go func() {

            actions <- newfv

        }()

    }

}

如果很多地方都需要这样做,建议为其创建一个辅助函数:


func safeSend(fv func()) {

    // First try to send with select:

    select {

    case actions <- fv:

        // Success!

    default:

        // Buffer is full, must do it in new goroutine:

        go func() {

            actions <- fv

        }()

    }

}

并使用它:


actions <- func() {

    safeSend(func() {

        // something to do

    })

}


查看完整回答
反对 回复 2021-12-20
  • 1 回答
  • 0 关注
  • 137 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信