Golang超时时出现Golang间歇性行为

时间:2019-07-10 08:05:38

标签: go concurrency goroutine channels

我正在尝试为重复性任务实现并发。我想在其他Goroutine(由longRunningTask函数实现)上实现http请求。我提供了一种计时器,用于在重载任务进行预定义的超时时停止Goroutine并向主Goroutine发送超时信号的机制。我目前遇到的问题是我的行为间歇性。

代码已简化为如下所示。

package main

import (
    "fmt"
    "time"
)

func main() {
    var iteration int = 5

    timeOutChan := make(chan struct{})
    resultChan := make(chan string)

    for i := 0; i < iteration; i++ {
        go longRunningTaks(timeOutChan, resultChan)
    }

    for i := 0; i < iteration; i++ {
        select {
        case data := <-resultChan:
            fmt.Println(data)
        case <-timeOutChan:
            fmt.Println("timed out")
        }
    }

}

func longRunningTaks(tc chan struct{}, rc chan string) {
    timer := time.NewTimer(time.Nanosecond * 1)
    defer timer.Stop()

    // Heavy load task
    time.Sleep(time.Second * 1)

    select {
    case <-timer.C:
        tc <- struct{}{}
    case rc <- "success":
        return
    }
}

我相信所有尝试都应该打印出来

timeout
timeout
timeout
timeout
timeout

相反,我断断续续

success
timeout
timeout
timeout
timeout

1 个答案:

答案 0 :(得分:-1)

医生提到:

  

NewTimer创建一个新的Timer,它将在其上发送当前时间   至少持续时间d 之后的频道。

“至少意味着”计时器肯定会占用指定的时间,但是这也暗含意味着可能花费比指定的时间更多的时间。计时器启动其自己的go例程,并在到期时写入通道。 由于调度程序或垃圾回收,写入其他通道的过程可能会延迟。考虑到上述可能性,模拟工作量非常短。

更新

正如彼得在评论中提到的那样,将“成功”写入rc通道的动作同样可能完成,因为它可以由主例程从另一端读取。选择必须在以下两者之间进行选择:1)将“成功”写入rc通道,以及2)过期计时器。两者都有可能。 开始时No1的可能性更大,因为主例程尚未从另一端读取它。一旦发生这种情况。其他剩余的例程将必须对该通道进行竞争(写入“成功”)(因为它正在使用缓冲区大小0进行阻塞),因此在其余时间中,选择过期定时器的可能性更大无法说主例程从resultChan通道(rc的另一端)读取的速度。