How to make the below code print "QUIT" after 3 seconds?
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)
}
}
}
The above code prints "Doing". and sleep 10 seconds, then print "QUIT". How to interrupt this sleep, let it receive quit
channel after 3 seconds and print "QUIT"?
It seems select
is blocked by case tasks
, and it will not receive from the quit
channel after 3 seconds.
To signal end of an asynchronous task it is a best practice to close the channel, this is rather important to prevent many missuse that leads to various deadlocks. In your original code I would have written close(quite)
rather than quit <- struct{}{}
Remember, read on a closed does not block and always return the zero value, that is the trick.
Anyways, appart from this, an elegant way to solve your problem is to use a combination of both context.Context
and time.After
.
time.After
will help you block on a selectable task set.
context.Context
is better suited to handle this kind of signals.
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):
}
}
}
}
The second job is running for 10 seconds, so it will block the loop for that time, and the signal you sent into the quit
channel will not be received until this job is complete.
To interrupt the time-consuming job, maybe you can split it into another goroutine. Something like this would do:
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")
It would be easier to use [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()
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.