Golang 为什么这个超时方案不起作用?

Golang why doesn't this timeout scheme work?

所以我有这个用于发送消息的代码块。传递给 c.outChan 的消息被传输,如果在 return 中收到确认,则 "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)
    }
}

我做错了什么?

您的频道正在使用开关,但您需要 select。该开关对通道一无所知,而只是尝试评估 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)
    }
}

另外,你的超时 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)
    }
}

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