【问题标题】:Golang why doesn't this timeout scheme work?Golang 为什么这个超时方案不起作用?
【发布时间】:2016-04-29 15:20:31
【问题描述】:

所以我有这个用于发送消息的代码块。传递给 c.outChan 的消息被传输,如果收到一个 ack 作为回报,“true”将通过 c.buffer[nr].signaler 通道传递。这似乎工作正常,但如果消息被丢弃(没有收到确认),而不是达到超时打印,它只是停止,我不知道为什么。代码如下:

func (c *uConnection) send(nr uint32) {
    //transmitt message
    c.outChan <- c.buffer[nr].msg
    timeout := make(chan bool, 1)
    go func() {
        timeoutTimer := time.After(c.retransTime)
        <-timeoutTimer
        timeout <- true
    }()
    switch {
    case <-c.buffer[nr].signaler:
        fmt.Printf("Ack confirmed: %v\n", nr)
    case <-timeout:
        fmt.Println("-----------timeout-----------\n")
        //resending
        c.send(nr)
    }
}

我做错了什么?

【问题讨论】:

    标签: go network-programming udp timeout


    【解决方案1】:

    您正在为您的频道使用一个开关,但您需要一个选择。 switch 对通道一无所知,而只是尝试在 select 之前评估 case 语句中的表达式。您当前的代码相当于:

    func (c *uConnection) send(nr uint32) {
        //transmitt message
        c.outChan <- c.buffer[nr].msg
        timeout := make(chan bool, 1)
        go func() {
            timeoutTimer := time.After(c.retransTime)
            <-timeoutTimer
            timeout <- true
        }()
        tmp1 := <-c.buffer[nr].signaler // this will block
        tmp2 := <-timeout
        switch {
        case tmp1 :
            fmt.Printf("Ack confirmed: %v\n", nr)
        case tmp2 :
            fmt.Println("-----------timeout-----------\n")
            //resending
            c.send(nr)
        }
    }
    

    您的代码应如下所示(使用 select 而不是 switch):

    func (c *uConnection) send(nr uint32) {
        //transmitt message
        c.outChan <- c.buffer[nr].msg
        timeout := make(chan bool, 1)
        go func() {
            timeoutTimer := time.After(c.retransTime)
            <-timeoutTimer
            timeout <- true
        }()
        select {
        case <-c.buffer[nr].signaler:
            fmt.Printf("Ack confirmed: %v\n", nr)
        case <-timeout:
            fmt.Println("-----------timeout-----------\n")
            //resending
            c.send(nr)
        }
    }
    

    你的 timeout goroutine 也是不必要的。而不是调用 time.After,在通道上等待然后发送到你自己的超时通道,你可以直接在 time.After 上等待。示例:

    func (c *uConnection) send(nr uint32) {
        //transmitt message
        c.outChan <- c.buffer[nr].msg
        select {
        case <-c.buffer[nr].signaler:
            fmt.Printf("Ack confirmed: %v\n", nr)
        case <-time.After(c.retransTime):
            fmt.Println("-----------timeout-----------\n")
            //resending
            c.send(nr)
        }
    }
    

    这样更快、更清晰并且使用更少的内存。

    【讨论】:

    • 我一直使用case &lt;- time.After(...)。作为我从其他开发人员那里得到的反馈(例如简单模式),我高度支持这种方法。
    猜你喜欢
    • 2015-10-05
    • 2015-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-11
    • 1970-01-01
    • 1970-01-01
    • 2023-04-05
    相关资源
    最近更新 更多