英文:
Ticker Stop behaviour in Golang
问题
如果我正在遍历一个ticker通道并调用stop(),通道会停止但不会关闭。
在这个例子中:
package main
import (
"time"
"log"
)
func main() {
ticker := time.NewTicker(1 * time.Second)
go func(){
for _ = range ticker.C {
log.Println("tick")
}
log.Println("stopped")
}()
time.Sleep(3 * time.Second)
log.Println("stopping ticker")
ticker.Stop()
time.Sleep(3 * time.Second)
}
运行结果为:
2013/07/22 14:26:53 tick
2013/07/22 14:26:54 tick
2013/07/22 14:26:55 tick
2013/07/22 14:26:55 stopping ticker
因此,该goroutine永远不会退出。有没有更好的处理这种情况的方法?我应该关心该goroutine永远不会退出吗?
英文:
If I am ranging over a ticker channel and call stop() the channel is stopped but not closed.
In this Example:
package main
import (
"time"
"log"
)
func main() {
ticker := time.NewTicker(1 * time.Second)
go func(){
for _ = range ticker.C {
log.Println("tick")
}
log.Println("stopped")
}()
time.Sleep(3 * time.Second)
log.Println("stopping ticker")
ticker.Stop()
time.Sleep(3 * time.Second)
}
Running produces:
2013/07/22 14:26:53 tick
2013/07/22 14:26:54 tick
2013/07/22 14:26:55 tick
2013/07/22 14:26:55 stopping ticker
So that goroutine never exits. Is there a better way to handle this case? Should I care that the goroutine never exited?
答案1
得分: 23
使用了第二个通道如Volker建议的。这是我最终运行的代码:
package main
import (
"log"
"time"
)
// 每个滴答运行一次函数
// 从函数中返回false以停止滴答器
func Every(duration time.Duration, work func(time.Time) bool) chan bool {
ticker := time.NewTicker(duration)
stop := make(chan bool, 1)
go func() {
defer log.Println("滴答器停止")
for {
select {
case time := <-ticker.C:
if !work(time) {
stop <- true
}
case <-stop:
return
}
}
}()
return stop
}
func main() {
stop := Every(1*time.Second, func(time.Time) bool {
log.Println("滴答")
return true
})
time.Sleep(3 * time.Second)
log.Println("停止滴答器")
stop <- true
time.Sleep(3 * time.Second)
}
英文:
Used a second channel as Volker suggested. This is what I ended up running with:
package main
import (
"log"
"time"
)
// Run the function every tick
// Return false from the func to stop the ticker
func Every(duration time.Duration, work func(time.Time) bool) chan bool {
ticker := time.NewTicker(duration)
stop := make(chan bool, 1)
go func() {
defer log.Println("ticker stopped")
for {
select {
case time := <-ticker.C:
if !work(time) {
stop <- true
}
case <-stop:
return
}
}
}()
return stop
}
func main() {
stop := Every(1*time.Second, func(time.Time) bool {
log.Println("tick")
return true
})
time.Sleep(3 * time.Second)
log.Println("stopping ticker")
stop <- true
time.Sleep(3 * time.Second)
}
答案2
得分: 15
在你的goroutine中,在ticker和done通道之间选择,并在第二个通道上发送信号“done”。
根据你真正想要做什么,可能存在更好的解决方案,但从简化的演示代码中很难判断。
英文:
Signal "done" on a second channel and select in your goroutine between ticker and done channel.
Depending on what you really want to do a better solution might exist, but this is hard to tell from the reduced demo code.
答案3
得分: 8
你可以这样做。
package main
import (
"fmt"
"time"
)
func startTicker(f func()) chan bool {
done := make(chan bool, 1)
go func() {
ticker := time.NewTicker(time.Second * 1)
defer ticker.Stop()
for {
select {
case <-ticker.C:
f()
case <-done:
fmt.Println("done")
return
}
}
}()
return done
}
func main() {
done := startTicker(func() {
fmt.Println("tick...")
})
time.Sleep(5 * time.Second)
close(done)
time.Sleep(5 * time.Second)
}
英文:
you can do like this.
package main
import (
"fmt"
"time"
)
func startTicker(f func()) chan bool {
done := make(chan bool, 1)
go func() {
ticker := time.NewTicker(time.Second * 1)
defer ticker.Stop()
for {
select {
case <-ticker.C:
f()
case <-done:
fmt.Println("done")
return
}
}
}()
return done
}
func main() {
done := startTicker(func() {
fmt.Println("tick...")
})
time.Sleep(5 * time.Second)
close(done)
time.Sleep(5 * time.Second)
}
答案4
得分: 0
如果你需要节省更多的空间,可以使用空结构体(struct{}
)的通道,它不占用内存。正如上面提到的,不要在其中发送任何东西,只需关闭通道,实际上发送的是零值。
英文:
If you need to save more space, use channels of empty structs (struct{}
) which costs no memory. And as mentioned above, don't send something in it - just close, which actually sends zero value.
答案5
得分: 0
我认为这是一个很好的答案
func doWork(interval time.Duration, work func(), stop chan struct{}, done func()) {
t := time.NewTicker(interval)
for {
select {
case <-t.C:
work()
case _, more := <-stop:
if !more {
done()
}
return
}
}
}
func Test_workTicker(t *testing.T) {
var wg sync.WaitGroup
wg.Add(1)
stop := make(chan struct{})
go doWork(
500*time.Millisecond,
func() { fmt.Println("做工作") },
stop,
func() {
fmt.Println("在go协程中完成")
wg.Done()
},
)
time.Sleep(5 * time.Second)
close(stop)
wg.Wait()
fmt.Println("在主函数中完成")
}
英文:
I think this is a nice answer
func doWork(interval time.Duration, work func(), stop chan struct{}, done func()) {
t := time.NewTicker(interval)
for {
select {
case <-t.C:
work()
case _, more := <-stop:
if !more {
done()
}
return
}
}
}
func Test_workTicker(t *testing.T) {
var wg sync.WaitGroup
wg.Add(1)
stop := make(chan struct{})
go doWork(
500*time.Millisecond,
func() { fmt.Println("do work") },
stop,
func() {
fmt.Println("done in go routine")
wg.Done()
},
)
time.Sleep(5 * time.Second)
close(stop)
wg.Wait()
fmt.Println("done in main")
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论