Home  >  Article  >  Backend Development  >  Safe way to terminate infinite looping goroutine?

Safe way to terminate infinite looping goroutine?

WBOY
WBOYforward
2024-02-06 09:09:04966browse

终止无限循环 goroutine 的安全方法?

Question content

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.

  1. Call Done() outside of an infinite for loop. This doesn't work because as far as I know the code is not accessible.

  2. 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.


Correct answer


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!

Statement:
This article is reproduced at:stackoverflow.com. If there is any infringement, please contact admin@php.cn delete