重置代码
Resetting a ticker
考虑一个使用 timer/ticker 资源初始化的服务器,该资源将在每个 t
(t
在我的示例中为 20 毫秒)滴答。每次服务器在网络上监听某些内容(例如来自对等方的周期性信号)时,它都必须重置计时器。另一方面,如果计时器在没有重置的情况下到期(例如,它的所有对等点都死了),它会触发一些事件(在我的示例中,我只是打印从程序开始的时间)。
我在使用 time.Ticker
实现此行为时遇到问题。重置计时器似乎有效(它不会在前 50 毫秒触发),但之后自动收报机不活动(不会每 20 毫秒打勾一次)。
package main
import (
"fmt"
"time"
)
var wallclock time.Time
type server struct {
timeout *time.Ticker
stop chan bool
}
func (srv *server) start() {
for {
select {
case <-srv.timeout.C:
{
elapsed := time.Since(wallclock)
fmt.Println("timed out after ", elapsed, " elapsed from start ")
}
case <-srv.stop:
{
return
}
}
}
}
func main() {
wallclock = time.Now()
//make the server with a timer that will fire every 20ms
srv := server{
timeout: time.NewTicker(20 * time.Millisecond),
//channel to indicate the server to stop listening
stop: make(chan bool),
}
//start listening on a different thread
go srv.start()
for i := 0; i < 5; i++ {
//reset it every 10ms
time.Sleep(10 * time.Millisecond)
srv.timeout.Stop()
//as the reset frequency is higher,
//I'm not expecting this to fire within
//the first 50ms (5*10ms)
srv.timeout = time.NewTicker(20 * time.Millisecond)
}
//sleep for 110ms
//I'm expecting the timer to fire at least 5 times here
time.Sleep(110 * time.Millisecond)
//stop listening
srv.stop <- true
fmt.Println("Hi from tckr!")
}
我期待看到类似
的内容
timed out after ~70ms elapsed from start
timed out after ~90ms elapsed from start
timed out after ~110ms elapsed from start
timed out after ~130ms elapsed from start
timed out after ~150ms elapsed from start
Hi from tckr!
五次,因为我让主线程休眠了 110 毫秒,而 20 毫秒计时器可以在此间隔内触发五次。
但我刚看到Hi from tckr!
。 srv.timeout = time.NewTicker(20 * time.Millisecond)
是重置 Ticker
的正确方法吗?
如果我不在 for
循环 (srv.timeout.Stop()
) 中停止自动收报机,则自动收报机似乎会继续运行。这是评论 srv.timeout.Stop()
后的示例输出。
timed out after 20.6872ms elapsed from start
timed out after 41.4278ms elapsed from start
timed out after 61.8747ms elapsed from start
timed out after 72.7793ms elapsed from start
timed out after 94.1448ms elapsed from start
timed out after 112.5283ms elapsed from start
timed out after 134.0131ms elapsed from start
timed out after 152.5846ms elapsed from start
Hi from tckr!
我不希望自动收报机在前 50 毫秒内触发(即,我不希望看到前两行分别为 20.6872 毫秒和 41.4278 毫秒)。
For all the cases in the statement, the channel operands of receive operations and the channel and right-hand-side expressions of send statements are evaluated exactly once, in source order, upon entering the "select" statement. The result is a set of channels to receive from or send to, and the corresponding values to send.
这意味着一旦 start
进入其 select
语句,它就会评估 srv.timeout.C
并保持通道; srv.timeout
进入 select
后对 srv.timeout
的任何更改都不会影响 select
,它仍将等待从之前的频道接收。
您可以通过添加另一个通道(在此处名为 ping
的示例中)来解决此问题,这样您就可以向 start
发出通道正在改变的信号(或者可能移动整个重置逻辑进入 start
):
type server struct {
timeout *time.Ticker
stop chan bool
ping chan struct{}
}
func (srv *server) start() {
for {
select {
case <-srv.timeout.C:
elapsed := time.Since(wallclock)
fmt.Println("timed out after ", elapsed, " elapsed from start ")
case <-srv.ping:
// do nothing & let the loop iterate
// OR
srv.timeout.Stop()
srv.timeout = time.NewTicker(20 * time.Millisecond)
case <-srv.stop:
return
}
}
}
// in main()
go srv.start()
for i := 0; i < 5; i++ {
//reset it every 10ms
time.Sleep(10 * time.Millisecond)
srv.ping <- struct{}{}
// possibly shift the below logic to start()'s ping handler case
srv.timeout.Stop()
srv.timeout = time.NewTicker(20 * time.Millisecond)
}
考虑一个使用 timer/ticker 资源初始化的服务器,该资源将在每个 t
(t
在我的示例中为 20 毫秒)滴答。每次服务器在网络上监听某些内容(例如来自对等方的周期性信号)时,它都必须重置计时器。另一方面,如果计时器在没有重置的情况下到期(例如,它的所有对等点都死了),它会触发一些事件(在我的示例中,我只是打印从程序开始的时间)。
我在使用 time.Ticker
实现此行为时遇到问题。重置计时器似乎有效(它不会在前 50 毫秒触发),但之后自动收报机不活动(不会每 20 毫秒打勾一次)。
package main
import (
"fmt"
"time"
)
var wallclock time.Time
type server struct {
timeout *time.Ticker
stop chan bool
}
func (srv *server) start() {
for {
select {
case <-srv.timeout.C:
{
elapsed := time.Since(wallclock)
fmt.Println("timed out after ", elapsed, " elapsed from start ")
}
case <-srv.stop:
{
return
}
}
}
}
func main() {
wallclock = time.Now()
//make the server with a timer that will fire every 20ms
srv := server{
timeout: time.NewTicker(20 * time.Millisecond),
//channel to indicate the server to stop listening
stop: make(chan bool),
}
//start listening on a different thread
go srv.start()
for i := 0; i < 5; i++ {
//reset it every 10ms
time.Sleep(10 * time.Millisecond)
srv.timeout.Stop()
//as the reset frequency is higher,
//I'm not expecting this to fire within
//the first 50ms (5*10ms)
srv.timeout = time.NewTicker(20 * time.Millisecond)
}
//sleep for 110ms
//I'm expecting the timer to fire at least 5 times here
time.Sleep(110 * time.Millisecond)
//stop listening
srv.stop <- true
fmt.Println("Hi from tckr!")
}
我期待看到类似
的内容timed out after ~70ms elapsed from start
timed out after ~90ms elapsed from start
timed out after ~110ms elapsed from start
timed out after ~130ms elapsed from start
timed out after ~150ms elapsed from start
Hi from tckr!
五次,因为我让主线程休眠了 110 毫秒,而 20 毫秒计时器可以在此间隔内触发五次。
但我刚看到Hi from tckr!
。 srv.timeout = time.NewTicker(20 * time.Millisecond)
是重置 Ticker
的正确方法吗?
如果我不在 for
循环 (srv.timeout.Stop()
) 中停止自动收报机,则自动收报机似乎会继续运行。这是评论 srv.timeout.Stop()
后的示例输出。
timed out after 20.6872ms elapsed from start
timed out after 41.4278ms elapsed from start
timed out after 61.8747ms elapsed from start
timed out after 72.7793ms elapsed from start
timed out after 94.1448ms elapsed from start
timed out after 112.5283ms elapsed from start
timed out after 134.0131ms elapsed from start
timed out after 152.5846ms elapsed from start
Hi from tckr!
我不希望自动收报机在前 50 毫秒内触发(即,我不希望看到前两行分别为 20.6872 毫秒和 41.4278 毫秒)。
For all the cases in the statement, the channel operands of receive operations and the channel and right-hand-side expressions of send statements are evaluated exactly once, in source order, upon entering the "select" statement. The result is a set of channels to receive from or send to, and the corresponding values to send.
这意味着一旦 start
进入其 select
语句,它就会评估 srv.timeout.C
并保持通道; srv.timeout
进入 select
后对 srv.timeout
的任何更改都不会影响 select
,它仍将等待从之前的频道接收。
您可以通过添加另一个通道(在此处名为 ping
的示例中)来解决此问题,这样您就可以向 start
发出通道正在改变的信号(或者可能移动整个重置逻辑进入 start
):
type server struct {
timeout *time.Ticker
stop chan bool
ping chan struct{}
}
func (srv *server) start() {
for {
select {
case <-srv.timeout.C:
elapsed := time.Since(wallclock)
fmt.Println("timed out after ", elapsed, " elapsed from start ")
case <-srv.ping:
// do nothing & let the loop iterate
// OR
srv.timeout.Stop()
srv.timeout = time.NewTicker(20 * time.Millisecond)
case <-srv.stop:
return
}
}
}
// in main()
go srv.start()
for i := 0; i < 5; i++ {
//reset it every 10ms
time.Sleep(10 * time.Millisecond)
srv.ping <- struct{}{}
// possibly shift the below logic to start()'s ping handler case
srv.timeout.Stop()
srv.timeout = time.NewTicker(20 * time.Millisecond)
}