[英]How to make this golang for-select code work?
如何使以下代码在 3 秒后打印“QUIT”?
package main
import (
"fmt"
"time"
)
func main() {
quit := make(chan struct{})
tasks := make(chan struct{})
go func(){
time.Sleep(1 * time.Second)
tasks <- struct{}{}
}()
go func(){
time.Sleep(3 * time.Second)
quit <- struct{}{}
}()
for {
select {
case <-quit:
fmt.Println("QUIT")
return
case <-tasks:
fmt.Println("Doing")
// do some long time jobs more than 10 seconds
time.Sleep(10 * time.Second)
}
}
}
上面的代码打印“Doing”。 并休眠 10 秒,然后打印“QUIT”。 如何中断此睡眠,让它在 3 秒后接收quit
频道并打印“QUIT”?
似乎select
被case tasks
阻塞,并且在 3 秒后它不会从quit
通道接收。
要发出异步任务结束的信号,最好关闭通道,这对于防止许多导致各种死锁的误用非常重要。 在您的原始代码中,我会写close(quite)
而不是quit <- struct{}{}
请记住,在关闭时读取不会阻塞并始终返回零值,这就是诀窍。
无论如何,除此之外,解决问题的一种优雅方法是结合使用context.Context
和time.After
。
time.After
将帮助您阻止可选择的任务集。
context.Context
更适合处理这种信号。
https://play.golang.org/p/ZVsZw3P-YHd
package main
import (
"context"
"log"
"time"
)
func main() {
log.Println("start")
defer log.Println("end")
ctx, cancel := context.WithCancel(context.Background())
tasks := make(chan struct{})
go func() {
time.Sleep(1 * time.Second) // a job
tasks <- struct{}{}
}()
go func() {
time.Sleep(3 * time.Second) // a job
cancel()
}()
for {
select {
case <-ctx.Done():
log.Println("QUIT")
return
case <-tasks:
log.Println("Doing")
// do some long time jobs more than 10 seconds
select {
case <-ctx.Done():
return
case <-time.After(time.Second * 10):
}
}
}
}
第二个作业运行了 10 秒,所以它会阻塞这段时间的循环,并且在这个作业完成之前不会收到你发送到quit
通道的信号。
为了打断这个耗时的工作,也许你可以把它拆分成另一个 goroutine。 这样的事情会做:
go func() {
for {
select {
case <-tasks:
fmt.Println("Doing")
// do some long time jobs more than 10 seconds
time.Sleep(10 * time.Second)
}
}
}()
<-quit
fmt.Println("QUIT")
使用 [sync.WaitGroup.
package main
import (
"fmt"
"sync"
"time"
)
func worker(msg string, duration time.Duration, doing bool, wg *sync.WaitGroup) {
defer wg.Done()
time.Sleep(duration)
fmt.Println(msg)
if doing {
time.Sleep(10 * time.Second)
}
}
func main() {
var wg sync.WaitGroup
msgs := [2]string{"QUIT", "Doing"}
durations := [2]time.Duration{3 * time.Second, 1 * time.Second}
doing := [2]bool{false, true}
for i, msg := range msgs {
wg.Add(1)
go worker(msg, durations[i], doing[i], &wg)
}
wg.Wait()
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.