所有goroutine都处于睡眠状态-死锁(无限循环+选择)

时间:2018-12-26 12:33:27

标签: go concurrency goroutine

我有一个应用程序,它每隔几秒钟就会创建一个例程,以从API获取当前价格。然后将响应发送到Monitor例程进行分析。如果监视器发现价格已发生重大变化,它将发送通知。

如果每个执行例程之间的延迟较大,则该方法很好。如果很小,则不是这样:“致命错误:所有goroutine都在睡着-死锁!”被触发,程序崩溃。

我正在猜测(?)死锁是由于以下两种原因之一:(1)监控器被新的价格信息所淹没(并且无法及时分析它们);或(2)主功能被监视器的消息淹没。

在Monitor迭代遍历Main函数时,Main函数会将新价格附加到切片中,这可能还存在一些缺点。

如何解决?在这里阅读其他文章,我认为“选择”语句将是万灵丹,但似乎并非如此。

func main() {
    ec := make(chan entry, 10)
    mce := make (chan entry, 10)
    mcn := make(chan string, 10)

    go monitor(mce, mcn)

    for {
        go fetchData(ec)
    select {
        // get entries
        case newEntry := <- ec:
            log = append(log, newEntry)
            mce <- newEntry
        default:
            {}
        }

        //check if any notifications received
        select {
        case newMsg := <- mcn:
            fmt.Println(newMsg)
        default:
            {}
        }

        delay()
    }
}

func monitor(mce <-chan entry, mcn chan<- string) {


    for {
        newEntry = <- mce

        for _, item := range log {
            // here - do some analysis comparing the newEntry against previous entries
            // (essentially to see if notification should be triggered)
        }

        if should_send_notification {
            mcn <- msg
        }

    }
}

func fetchData(ec chan<- entry) {

    // here some code fetching newEntry from APIs

    // send the newEntry back to the main function
    ec <- newEntry

}

1 个答案:

答案 0 :(得分:0)

您只需要合并一个select并删除main函数上的默认语句。删除默认语句后,您就不需要delay()函数,因为select-case会阻止并等待通道中的某些消息:

func main() {
    ec := make(chan entry, 10)
    mce := make (chan entry, 10)
    mcn := make(chan string, 10)

    go monitor(mce, mcn)
    go fetchData(ec)
    for {
        select {
            // get entries
            case newEntry := <- ec:
                log = append(log, newEntry)
                mce <- newEntry
            //check if any notifications received
            case newMsg := <- mcn:
                fmt.Println(newMsg)
        }
    }
}

fetchData(ec)可以方便地实现类似阻塞的操作,并且不必连续调用它:

func fetchData(ec chan<- entry) {

    for {
        // here some code fetching newEntry from APIs
        // waiting data

        // send the newEntry if I get data
        ec <- newEntry
    }

}