使用 timer.Reset() 的计时器示例未按描述工作
Timer example using timer.Reset() not working as described
我一直在使用示例来尝试获得我的第一个 "go routine" 运行,而当我得到它时 运行,它不会按照 go 文档的规定工作timer.Reset() 函数。
就我而言,我相信我这样做的方式很好,因为我实际上并不关心 chan 缓冲区中的内容(如果有的话)。如果 case _, ok := <-watcher.Events:
上发生任何事情,那么所有这一切都是为了触发 case <-tmr.C:
,然后一切都安静至少一秒钟。这样做的原因是 case _, ok := <-watcher.Events:
可以从一个到几十个微秒之间发生事件,我只关心一旦它们全部完成并且事情再次稳定下来。
不过,我担心按照文档中的说明进行操作 "must do" 是行不通的。如果我知道做得更好,我会说文档有缺陷,因为它假设缓冲区中有某些东西,但可能没有,但我不知道做得好到足以有信心做出决定,所以我希望一些专家出来有没有可以开导一下。
下面是代码。我没有把它放在操场上,因为我必须做一些清理(删除对程序其他部分的调用)并且我不确定我将如何让它对文件系统更改做出反应以显示它工作。
我已经在代码中清楚地标记了哪些备选方案有效,哪些无效。
func (pm *PluginManager) LoadAndWatchPlugins() error {
// DOING OTHER STUFF HERE
fmt.Println(`m1`)
done := make(chan interface{})
terminated := make(chan interface{})
go pm.watchDir(done, terminated, nil)
fmt.Println(`m2.pre-10`)
time.Sleep(10 * time.Second)
fmt.Println(`m3-post-10`)
go pm.cancelWatchDir(done)
fmt.Println(`m4`)
<-terminated
fmt.Println(`m5`)
os.Exit(0) // Temporary for testing
return Err
}
func (pm *PluginManager) cancelWatchDir(done chan interface{}) {
fmt.Println(`t1`)
time.Sleep(5 * time.Second)
fmt.Println()
fmt.Println(`t2`)
close(done)
}
func (pm *PluginManager) watchDir(done <-chan interface{}, terminated chan interface{}, strings <-chan string) {
watcher, err := fsnotify.NewWatcher()
if err != nil {
Logger("watchDir::"+err.Error(), `plugins`, Error)
}
//err = watcher.Add(pm.pluginDir)
err = watcher.Add(`/srv/plugins/`)
if err != nil {
Logger("watchDir::"+err.Error(), `plugins`, Error)
}
var tmr = time.NewTimer(time.Second)
tmr.Stop()
defer close(terminated)
defer watcher.Close()
defer tmr.Stop()
for {
select {
case <-tmr.C:
fmt.Println(`UPDATE FIRED`)
tmr.Stop()
case _, ok := <-watcher.Events:
if !ok {
return
}
fmt.Println(`Ticker: STOP`)
/*
* START OF ALTERNATIVES
*
* THIS IS BY EXAMPLE AND STATED THAT IT "MUST BE" AT:
* https://golang.org/pkg/time/#Timer.Reset
*
* BUT DOESN'T WORK
*/
if !tmr.Stop() {
fmt.Println(`Ticker: CHAN DRAIN`)
<-tmr.C // STOPS HERE AND GOES NO FURTHER
}
/*
* BUT IF I JUST DO THIS IT WORKS
*/
tmr.Stop()
/*
* END OF ALTERNATIVES
*/
fmt.Println(`Ticker: RESET`)
tmr.Reset(time.Second)
case <-done:
fmt.Println(`DONE TRIGGERED`)
return
}
}
}
您创建了一个计时器并立即停止它:
var tmr = time.NewTimer(time.Second)
tmr.Stop()
这没有任何意义,我认为这只是您的 "accident"。
但更进一步,在你的循环中:
case _, ok := <-watcher.Events:
发生这种情况时,您声称这不起作用:
if !tmr.Stop() {
fmt.Println(`Ticker: CHAN DRAIN`)
<-tmr.C // STOPS HERE AND GOES NO FURTHER
}
Timer.Stop()
记录它 returns true
如果这个调用停止计时器, false
如果计时器已经停止(或过期)。但是你的计时器在创建后就已经停止了,所以 tmr.Stop()
returns false
正确,所以你进入 if
并尝试从 tmr.C
接收,但由于计时器已 "long" 停止,因此不会在其通道上发送任何内容,因此这是一个阻塞(永远)操作。
如果您是使用 timer.Stop()
明确停止计时器的人,建议的 "pattern" 耗尽其频道没有任何意义,并且不适用于第二个 Timer.Stop()
打电话。
你没有使用 time.Timer。
尝试像这样简单的事情
package main
import (
"fmt"
"log"
"time"
)
func main() {
timer := time.NewTimer(time.Second)
watcher := make(chan bool)
done := make(chan bool)
evs := make(chan bool)
go func() {
trigger := false
for {
select {
case <-timer.C:
if trigger {
trigger = false
evs <- true
}
timer.Reset(time.Second)
case _, ok := <-watcher:
if !ok {
return
}
trigger = true
case <-done:
fmt.Println(`DONE TRIGGERED`)
return
}
}
}()
go func() {
for e := range evs {
log.Println("e", e)
}
}()
// simulate multiple events
watcher <- true
watcher <- true
watcher <- true
watcher <- true
<-time.After(time.Second + time.Millisecond*100)
watcher <- true
watcher <- true
watcher <- true
<-time.After(time.Second + time.Millisecond*100)
fmt.Println("Hello, playground")
}
除了 (q.v.), note that the documentation 所说的:
For example, assuming the program has not received from t.C already:
if !t.Stop() {
<-t.C
}
This cannot be done concurrent to other receives from the Timer's channel.
有人可能会争辩说这不是一个很好的例子,因为它假定在您调用 t.Stop
时计时器是 运行。但它确实继续提到,如果已经有一些现有的 goroutine 正在或可能正在从 t.C
.
中读取,那么这是一个 糟糕的 想法
(Reset
文档重复了所有这些,并且顺序错误,因为 Reset
在 Stop
之前排序。)
本质上整个区域有点fraught。没有好的通用答案,因为在 return 从 t.Stop
回到你的电话期间至少有三种可能的情况:
- 没有人在收听频道,现在频道中没有计时器。如果计时器在 调用
t.Stop
之前 已经停止,则通常会出现这种情况。如果计时器已经停止,t.Stop
总是 returns false。
- 没有人在收听频道,现在频道中有计时器。当计时器为 运行 但
t.Stop
无法阻止它启动时,情况总是如此。在这种情况下,t.Stop
return 为假。当计时器 是 运行 但在 你甚至调用 t.Stop
之前触发 并且因此停止时也是这种情况它自己的,所以 t.Stop
无法阻止它并且 returned false.
- 其他人正在收听频道。
在最后一种情况下,你什么都不应该做。在第一种情况下,你什么都不应该做。在第二种情况下,您可能希望从通道接收以便将其清除。这就是他们的榜样。
有人可能会争辩说:
if !t.Stop() {
select {
case <-t.C:
default:
}
}
是一个更好的例子。它执行一次非阻塞尝试,如果存在则消耗计时器滴答声,如果没有计时器滴答声则不执行任何操作。当您调用 t.Stop
时,无论计时器是否实际上是 运行,这都有效。事实上,它甚至可以在 t.Stop
returns true
的情况下工作,尽管在那种情况下,t.Stop
停止了计时器,因此计时器从未设法将计时器滴答放入渠道。 (因此,如果通道中有数据,它一定是之前清除通道失败遗留下来的。如果没有这样的错误,则接收尝试也没有必要。)
但是,如果 其他人——其他 goroutine——正在或可能正在阅读频道,你根本不应该这样做。尽管调用了 Stop
.
,但无法知道谁(您或他们)会得到频道中可能存在的任何计时器滴答声
与此同时,如果您不打算继续使用计时器,那么在频道中留下一个计时器滴答声(如果有的话)相对无害。当通道本身被垃圾回收时,它将被垃圾回收。当然,这是否明智取决于您对计时器的处理方式,但在这些情况下,只需调用 t.Stop
并忽略其 return 值就足够了。
我一直在使用示例来尝试获得我的第一个 "go routine" 运行,而当我得到它时 运行,它不会按照 go 文档的规定工作timer.Reset() 函数。
就我而言,我相信我这样做的方式很好,因为我实际上并不关心 chan 缓冲区中的内容(如果有的话)。如果 case _, ok := <-watcher.Events:
上发生任何事情,那么所有这一切都是为了触发 case <-tmr.C:
,然后一切都安静至少一秒钟。这样做的原因是 case _, ok := <-watcher.Events:
可以从一个到几十个微秒之间发生事件,我只关心一旦它们全部完成并且事情再次稳定下来。
不过,我担心按照文档中的说明进行操作 "must do" 是行不通的。如果我知道做得更好,我会说文档有缺陷,因为它假设缓冲区中有某些东西,但可能没有,但我不知道做得好到足以有信心做出决定,所以我希望一些专家出来有没有可以开导一下。
下面是代码。我没有把它放在操场上,因为我必须做一些清理(删除对程序其他部分的调用)并且我不确定我将如何让它对文件系统更改做出反应以显示它工作。
我已经在代码中清楚地标记了哪些备选方案有效,哪些无效。
func (pm *PluginManager) LoadAndWatchPlugins() error {
// DOING OTHER STUFF HERE
fmt.Println(`m1`)
done := make(chan interface{})
terminated := make(chan interface{})
go pm.watchDir(done, terminated, nil)
fmt.Println(`m2.pre-10`)
time.Sleep(10 * time.Second)
fmt.Println(`m3-post-10`)
go pm.cancelWatchDir(done)
fmt.Println(`m4`)
<-terminated
fmt.Println(`m5`)
os.Exit(0) // Temporary for testing
return Err
}
func (pm *PluginManager) cancelWatchDir(done chan interface{}) {
fmt.Println(`t1`)
time.Sleep(5 * time.Second)
fmt.Println()
fmt.Println(`t2`)
close(done)
}
func (pm *PluginManager) watchDir(done <-chan interface{}, terminated chan interface{}, strings <-chan string) {
watcher, err := fsnotify.NewWatcher()
if err != nil {
Logger("watchDir::"+err.Error(), `plugins`, Error)
}
//err = watcher.Add(pm.pluginDir)
err = watcher.Add(`/srv/plugins/`)
if err != nil {
Logger("watchDir::"+err.Error(), `plugins`, Error)
}
var tmr = time.NewTimer(time.Second)
tmr.Stop()
defer close(terminated)
defer watcher.Close()
defer tmr.Stop()
for {
select {
case <-tmr.C:
fmt.Println(`UPDATE FIRED`)
tmr.Stop()
case _, ok := <-watcher.Events:
if !ok {
return
}
fmt.Println(`Ticker: STOP`)
/*
* START OF ALTERNATIVES
*
* THIS IS BY EXAMPLE AND STATED THAT IT "MUST BE" AT:
* https://golang.org/pkg/time/#Timer.Reset
*
* BUT DOESN'T WORK
*/
if !tmr.Stop() {
fmt.Println(`Ticker: CHAN DRAIN`)
<-tmr.C // STOPS HERE AND GOES NO FURTHER
}
/*
* BUT IF I JUST DO THIS IT WORKS
*/
tmr.Stop()
/*
* END OF ALTERNATIVES
*/
fmt.Println(`Ticker: RESET`)
tmr.Reset(time.Second)
case <-done:
fmt.Println(`DONE TRIGGERED`)
return
}
}
}
您创建了一个计时器并立即停止它:
var tmr = time.NewTimer(time.Second)
tmr.Stop()
这没有任何意义,我认为这只是您的 "accident"。
但更进一步,在你的循环中:
case _, ok := <-watcher.Events:
发生这种情况时,您声称这不起作用:
if !tmr.Stop() {
fmt.Println(`Ticker: CHAN DRAIN`)
<-tmr.C // STOPS HERE AND GOES NO FURTHER
}
Timer.Stop()
记录它 returns true
如果这个调用停止计时器, false
如果计时器已经停止(或过期)。但是你的计时器在创建后就已经停止了,所以 tmr.Stop()
returns false
正确,所以你进入 if
并尝试从 tmr.C
接收,但由于计时器已 "long" 停止,因此不会在其通道上发送任何内容,因此这是一个阻塞(永远)操作。
如果您是使用 timer.Stop()
明确停止计时器的人,建议的 "pattern" 耗尽其频道没有任何意义,并且不适用于第二个 Timer.Stop()
打电话。
你没有使用 time.Timer。
尝试像这样简单的事情
package main
import (
"fmt"
"log"
"time"
)
func main() {
timer := time.NewTimer(time.Second)
watcher := make(chan bool)
done := make(chan bool)
evs := make(chan bool)
go func() {
trigger := false
for {
select {
case <-timer.C:
if trigger {
trigger = false
evs <- true
}
timer.Reset(time.Second)
case _, ok := <-watcher:
if !ok {
return
}
trigger = true
case <-done:
fmt.Println(`DONE TRIGGERED`)
return
}
}
}()
go func() {
for e := range evs {
log.Println("e", e)
}
}()
// simulate multiple events
watcher <- true
watcher <- true
watcher <- true
watcher <- true
<-time.After(time.Second + time.Millisecond*100)
watcher <- true
watcher <- true
watcher <- true
<-time.After(time.Second + time.Millisecond*100)
fmt.Println("Hello, playground")
}
除了
For example, assuming the program has not received from t.C already:
if !t.Stop() { <-t.C }
This cannot be done concurrent to other receives from the Timer's channel.
有人可能会争辩说这不是一个很好的例子,因为它假定在您调用 t.Stop
时计时器是 运行。但它确实继续提到,如果已经有一些现有的 goroutine 正在或可能正在从 t.C
.
(Reset
文档重复了所有这些,并且顺序错误,因为 Reset
在 Stop
之前排序。)
本质上整个区域有点fraught。没有好的通用答案,因为在 return 从 t.Stop
回到你的电话期间至少有三种可能的情况:
- 没有人在收听频道,现在频道中没有计时器。如果计时器在 调用
t.Stop
之前 已经停止,则通常会出现这种情况。如果计时器已经停止,t.Stop
总是 returns false。 - 没有人在收听频道,现在频道中有计时器。当计时器为 运行 但
t.Stop
无法阻止它启动时,情况总是如此。在这种情况下,t.Stop
return 为假。当计时器 是 运行 但在 你甚至调用t.Stop
之前触发 并且因此停止时也是这种情况它自己的,所以t.Stop
无法阻止它并且 returned false. - 其他人正在收听频道。
在最后一种情况下,你什么都不应该做。在第一种情况下,你什么都不应该做。在第二种情况下,您可能希望从通道接收以便将其清除。这就是他们的榜样。
有人可能会争辩说:
if !t.Stop() {
select {
case <-t.C:
default:
}
}
是一个更好的例子。它执行一次非阻塞尝试,如果存在则消耗计时器滴答声,如果没有计时器滴答声则不执行任何操作。当您调用 t.Stop
时,无论计时器是否实际上是 运行,这都有效。事实上,它甚至可以在 t.Stop
returns true
的情况下工作,尽管在那种情况下,t.Stop
停止了计时器,因此计时器从未设法将计时器滴答放入渠道。 (因此,如果通道中有数据,它一定是之前清除通道失败遗留下来的。如果没有这样的错误,则接收尝试也没有必要。)
但是,如果 其他人——其他 goroutine——正在或可能正在阅读频道,你根本不应该这样做。尽管调用了 Stop
.
与此同时,如果您不打算继续使用计时器,那么在频道中留下一个计时器滴答声(如果有的话)相对无害。当通道本身被垃圾回收时,它将被垃圾回收。当然,这是否明智取决于您对计时器的处理方式,但在这些情况下,只需调用 t.Stop
并忽略其 return 值就足够了。