use*_*087 4 timer channel go routines
我一直在尝试让我的第一个“go 例程”运行的示例,当我让它运行时,它不会按照 go 文档中规定的带有 timer.Reset() 函数的方式工作。
就我而言,我相信我这样做的方式很好,因为我实际上并不关心 chan 缓冲区中有什么(如果有的话)。这样做的目的是,case <-tmr.C:
如果发生任何事情,就会触发case _, ok := <-watcher.Events:
,然后一切都会安静至少一秒钟。这样做的原因是,case _, ok := <-watcher.Events:
事件的间隔可能是一到几十微秒,我只关心它们全部完成并且事情再次稳定下来。
然而我担心按照文档所说的“必须做”的方式做是行不通的。如果我知道做得更好,我会说文档是有缺陷的,因为它假设缓冲区中有东西,而实际上可能没有,但我不知道做得足够好,有信心做出这样的决定,所以我希望一些专家出来那里可以启发我。
下面是代码。我还没有把它放在操场上,因为我必须做一些清理(删除对程序其他部分的调用),并且我不确定如何让它对文件系统更改做出反应以显示它的工作。
我已经在代码中清楚地标记了哪些替代方案有效,哪些无效。
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
}
}
}
Run Code Online (Sandbox Code Playgroud)
\n\n\n例如,假设程序尚未从 tC 接收到:
\n\nRun Code Online (Sandbox Code Playgroud)\n\nif !t.Stop() {\n <-t.C\n}\n
这不能与来自计时器通道的其他接收同时完成。
\n
有人可能会说这不是一个很好的例子,因为它假设计时器在您调用时正在运行t.Stop
。但它确实继续提到,如果已经有一些现有的 goroutine 正在或可能正在读取t.C
.
(Reset
文档重复了所有这些,并且顺序有点错误,因为Reset
排序在 之前Stop
。)
本质上,整个地区都有点令人担忧。没有好的通用答案,因为在返回t.Stop
呼叫期间至少存在三种可能的情况:
t.Stop
已停止,通常会出现这种情况。如果计时器已经停止,t.Stop
则始终返回 false。t.Stop
无法阻止其触发时,总是会出现这种情况。在这种情况下,t.Stop
返回 false。当计时器正在运行但在您调用之前t.Stop
就触发了,因此它自行停止了,因此也是这种情况t.Stop
无法停止它并返回 false 。在最后一种情况下,您不应该执行任何操作。在第一种情况下,您不应该执行任何操作。在第二种情况下,您可能希望从通道接收以便将其清除。这就是他们的例子的目的。
\n\n有人可能会争辩说:
\n\nif !t.Stop() {\n select {\n case <-t.C:\n default:\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n是一个更好的例子。它执行一次非阻塞尝试,该尝试将消耗计时器滴答(如果存在),如果没有计时器滴答,则不执行任何操作。无论您调用 时计时器是否实际运行,这都有效t.Stop
。事实上,如果t.Stop
returns ,它甚至可以工作true
,尽管在这种情况下,t.Stop
停止了计时器,因此计时器从未设法将计时器滴答放入通道中。(因此,如果通道中存在数据,则它必然是先前清除通道失败所遗留的。如果不存在此类错误,则尝试接收也就不必要了。)
但是,如果其他人\xe2\x80\x94 或其他 goroutine\xe2\x80\x94 正在读取通道,则您根本不应该执行任何操作。尽管调用了 ,但无法知道谁(您或他们)将获得通道中可能存在的任何计时器滴答声Stop
。
同时,如果您不打算进一步使用计时器,那么在通道中留下一个计时器滴答声(如果有的话)相对无害。当通道本身被垃圾收集时,它也会被垃圾收集。当然,这是否明智取决于您对计时器所做的事情,但在这些情况下,只需调用t.Stop
并忽略其返回值就足够了。
归档时间: |
|
查看次数: |
3037 次 |
最近记录: |