Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Closing stop channel does not stop goroutines

INTRODUCTION:

I am just starting to learn the Go language and have reached the lesson about concurrency.

I have invented a small task for myself to try to implement what I have learned about closing goroutines.

PROBLEM:

If we close channel it's case will always be selected in the select statement, which is a great way to broadcast cancelation signal to all goroutines.

Below I have 2 goroutines and a quit channel that never gets received.

Code timeouts on Playground.

If I comment out default part in the goroutines then quit signal gets received.

QUESTION:

I really do not understand why is this happening and how to fix it, although I am trying.
Can somebody please explain me what is the problem and offer some advice on how to solve it?

package main

import (
    "fmt"
    "sync"
    "time"
)

func positive_numbers(quit chan struct{}, wg *sync.WaitGroup) {
    defer wg.Done()
    i := 1
    for {
        select {
        case <-quit:
            fmt.Println("[+]Quiting...")
            return
        default:
            fmt.Printf("%v ", i)
            i++
        }
    }
}

func negative_numbers(quit chan struct{}, wg *sync.WaitGroup) {
    defer wg.Done()
    i := -1
    for {
        select {
        case <-quit:
            fmt.Println("[-]Quiting...")
            return
        default:
            fmt.Printf("%v ", i)
            i--
        }
    }
}

func main() {
    quit := make(chan struct{})

    wg := sync.WaitGroup{} // so we can wait for all goroutines to finish
    wg.Add(2)

    go positive_numbers(quit, &wg)
    go negative_numbers(quit, &wg)

    go func(quit chan struct{}) {
        defer close(quit)
        time.Sleep(1 * time.Second)
    }(quit)

    wg.Wait()
}
like image 897
AlwaysLearningNewStuff Avatar asked Aug 13 '21 07:08

AlwaysLearningNewStuff


People also ask

How do you stop Goroutines?

You can't kill a goroutine from outside. You can signal a goroutine to stop using a channel, but there's no handle on goroutines to do any sort of meta management. Goroutines are intended to cooperatively solve problems, so killing one that is misbehaving would almost never be an adequate response.

What happens if you close a closed channel Golang?

We can close a channel in Golang with the help of the close() function. Once a channel is closed, we can't send data to it, though we can still read data from it. A closed channel denotes a case where we want to show that the work has been done on this channel, and there's no need for it to be open.

How do I gracefully close a channel?

The Channel Closing Principle One general principle of using Go channels is don't close a channel from the receiver side and don't close a channel if the channel has multiple concurrent senders. In other words, we should only close a channel in a sender goroutine if the sender is the only sender of the channel.

Do I need to close channel Golang?

You needn't close every channel when you've finished with it. It's only necessary to close a channel when it is important to tell the receiving goroutines that all data have been sent.


1 Answers

This code works alright in real life; it's just not compatible with the playground's "fake time" because positive_numbers and negative_numbers don't block, and the runtime can't decide how many iterations of each get to run before the Sleep expires (basically, the prints take zero emulated wallclock time, so they try to produce infinite output, and you hit the playground CPU usage limit without ever advancing the wallclock time at all). On real machines, where you can only print finite output in finite time, you get reasonable behavior.

In the playground, if you add something like time.Sleep(time.Millisecond) after each print, you force the fake clock to move forward, and the program also terminates.

like image 177
hobbs Avatar answered Oct 14 '22 05:10

hobbs