使用time.After进行超时处理与使用ticker或timer进行超时处理的行为不同。

huangapple go评论86阅读模式
英文:

timing out with time.After does not behave like timing out with a ticker or a timer

问题

我期望以下两个函数的行为是相同的:

func fillChanTimeoutUsingTicker(maxDuration time.Duration, chanSize int) chan string {
	c := make(chan string, chanSize)
	ticker := time.NewTicker(maxDuration)
	for {
		select {
		case <-ticker.C:
			ticker.Stop()
			fmt.Println("Ticker: operation timed out")
			return c
		case c <- "Random message":
		default:
			fmt.Println("Ticker: chan is full")
			return c
		}
	}
}

func fillChanTimeoutUsingTimeAfter(maxDuration time.Duration, chanSize int) chan string {
	c := make(chan string, chanSize)
	for {
		select {
		case <-time.After(maxDuration):
			fmt.Println("time.After: operation timed out")
			return c
		case c <- "Random message":
		default:
			fmt.Println("time.After: chan is full")
			return c
		}
	}
}

调用它们的方式是:

resWithTicker := fillChanTimeoutUsingTicker(time.Duration(1*time.Microsecond), 10000000)
fmt.Println(len(resWithTicker))
resWithTimeAfter := fillChanTimeoutUsingTimeAfter(time.Duration(1*time.Microsecond), 10000000)
fmt.Println(len(resWithTimeAfter))

打印结果为:

Ticker: operation timed out
43979
time.After: chan is full
10000000

我原以为它们的行为会完全相同,但实际上它们之间有很大的差异,对此有什么想法吗?

另外,使用计时器(timer)的方式与使用 ticker 的方式一样符合预期。

英文:

I expect the follwoing to functions to behave the same way

func fillChanTimeoutUsingTicker(maxDuration time.Duration, chanSize int) chan string {
	c := make(chan string, chanSize)
	ticker := time.NewTicker(maxDuration)
	for {
		select {
		case &lt;-ticker.C:
			ticker.Stop()
			fmt.Println(&quot;Ticker:operation timedout&quot;)
			return c
		case c &lt;- &quot;Random message&quot;:
		default:
			fmt.Println(&quot;Ticker:chan is full&quot;)
			return c
		}
	}
}

func fillChanTimeoutUsingTimeAfter(maxDuration time.Duration, chanSize int) chan string {
	c := make(chan string, chanSize)
	for {
		select {
		case &lt;-time.After(maxDuration):
			fmt.Println(&quot;time.After:operation timedout&quot;)
			return c
		case c &lt;- &quot;Random message&quot;:
		default:
			fmt.Println(&quot;time.After:chan is full&quot;)
			return c
		}
	}
}

calling them as :

    resWithTicker := fillChanTimeoutUsingTicker(time.Duration(1*time.Microsecond), 10000000)
	fmt.Println(len(resWithTicker))
	resWithTimeAfter := fillChanTimeoutUsingTimeAfter(time.Duration(1*time.Microsecond), 10000000)
	fmt.Println(len(resWithTimeAfter))

prints:

Ticker:operation timedout
43979
time.After:chan is full
10000000

i thought that they would behave exactly the same way and i really don't get the huge difference, any thoughts on this?

note also using a timer works as expected like in the ticker function.

答案1

得分: 3

问题出在你的代码中。

在你的第一个示例中,你创建了一个计时器并将其用于超时。在你的第二个示例中,你在每次循环时都创建一个计时器:

case <-time.After(maxDuration):

根据库源代码可以看出,这等同于:

case <- time.NewTimer(maxDuration).C:

如果你在每次循环时都创建一个新的 Ticker/Timer(并且丢弃旧的),它可能永远不会触发。

因此,要使你的第二个示例正确运行,可以按照以下方式编写代码(未经测试):

func fillChanTimeoutUsingTimeAfter(maxDuration time.Duration, chanSize int) chan string {
    c := make(chan string, chanSize)
    t := time.After(maxDuration)
    for {
        select {
        case <-t:
            fmt.Println("time.After: operation timed out")
            return c
        case c <- "Random message":
        default:
            fmt.Println("time.After: chan is full")
            return c
        }
    }
}
英文:

The problem lies within your code.

In your first example, you are creating one ticker and use that for timing out.
In your second example, you create a timer every time you loop:

case &lt;-time.After(maxDuration):

As can be seen in the library sources, this is equivalent to

case &lt;- time.NewTimer(maxDuration).C:

If you create a new Ticker/Timer every time you loop (and discard the old one), it will probably never fire.

So, to get your second example to behave correctly, do it like this (untested):

func fillChanTimeoutUsingTimeAfter(maxDuration time.Duration, chanSize int) chan string {
    c := make(chan string, chanSize)
    t := time.After(maxDuration)
    for {
        select {
        case &lt;-t:
            fmt.Println(&quot;time.After:operation timedout&quot;)
            return c
        case c &lt;- &quot;Random message&quot;:
        default:
            fmt.Println(&quot;time.After:chan is full&quot;)
            return c
        }
    }
}

huangapple
  • 本文由 发表于 2015年9月19日 20:33:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/32668268.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定