I have a goroutine that acts as a listener. The input stream comes into some buffered channel
, and I want my goroutine to handle the data coming into that channel. However, sometimes channel
may be temporarily without data input. If the channel
has nothing to offer for a second, I want my goroutine to do something different. The function looks like this:
func main () { var wg sync.WaitGroup arr := make([]*myObject, 0) wg.Add(1) go listener(c, arr, &wg) for { // sending stuff to c } } func listener(c chan *myObject, arr []*myObject, wg *sync.WaitGroup) { for { select { case value := <- c: arr = append(arr, value) case <- time.After(1 * time.Second): fmt.Println(arr) } }
The problem is that I want to see everything that goes through that channel printed. If main
ends suddenly, there may be something left in arr
that hasn't been printed yet and I won't be able to see it. So I need to make sure that this goroutine processes all the data in the channel before the program ends. I think this means I need to use WaitGroup
and use Wait()
to make sure the program doesn't shut down before my goroutine has finished what it needs to do. But I don't know where Done()
is called on my WaitGroup
.
Basically, I need a safe way to "pause" the goroutine before the program ends, and print out what's left. How can I do this?
Worse, for things like unit testing, I send the data to the channel myself, and after sending a certain amount, I want to view the array. However, if I just check the array right after the code that sends the data to the channel, the goroutine may not have had a chance to process all the data yet. In this case, I want to wait for the goroutine to process all the data I send, and then I want to pause it and ask it to show me the array. But how do I know when the goroutine has finished processing? I could sleep
for a while, give it a chance to finish, and then stop and look, but that feels pretty hacky. I think there is a best practice way to solve this problem, but I haven't figured it out yet.
Here are some ideas I had, zero of them worked.
Call Done()
outside of an infinite for
loop. This doesn't work because as far as I know the code is not accessible.
Call Done()
in a timeout case
. This doesn't work because after the timeout there may be more data on the way and I want my goroutine to keep listening.
Modify the listener to return when the channel is closed. Call wg.Done() on return:
func listener(c chan *myObject, arr []*myObject, wg *sync.WaitGroup) { defer wg.Done() for { select { case value, ok := <- c: if !ok { return } arr = append(arr, value) case <- time.After(1 * time.Second): fmt.Println(arr) } }
Modify main to close the channel after sending is complete. Wait for the goroutine to complete before returning from main.
var wg sync.WaitGroup arr := make([]*myObject, 0) wg.Add(1) go listener(c, arr, &wg) for { // sending stuff to c } close(c) wg.Wait()
The above is the detailed content of Safe way to terminate infinite looping goroutine?. For more information, please follow other related articles on the PHP Chinese website!