Home > OS >  Changing channel from unbuffered to buffered prevents goroutine from running
Changing channel from unbuffered to buffered prevents goroutine from running

Time:02-12

Here is an exercise using channels and select in a goroutine. If the disconnect channel is changed to a buffered channel the goroutine doesn't run at all.

Why does changing from an unbuffered to a buffered channel prevent running the goroutine?

func SelectDemo(wg *sync.WaitGroup) {

    messageCh := make(chan int, 10)
    disconnectCh := make(chan struct{})
    //  go routine won't run if channel is buffered
    //disconnectCh := make(chan struct{}, 1)

    defer close(messageCh)
    defer close(disconnectCh)
    go func() {
        fmt.Println("  goroutine")
        wg.Add(1)
        for {
            select {
            case v := <-messageCh:
                fmt.Println(v)
            case <-disconnectCh:
                fmt.Println("  disconnectCh")
                //  empty the buffered channel before exiting
                for {
                    select {
                    case v := <-messageCh:
                        fmt.Println(v)
                    default:
                        fmt.Println("  disconnection, return")
                        wg.Done()
                        return
                    }
                }
            }
        }
    }()

    fmt.Println("Sending ints")
    for i := 0; i < 10; i   {
        messageCh <- i
    }

    fmt.Println("Sending done")
    disconnectCh <- struct{}{}
}

Here's the code to call the function from main. I use the wait group to assure that the goroutine completes before the program exits:

wg := sync.WaitGroup{}
ch09.SelectDemo(&wg)
wg.Wait()

CodePudding user response:

That code logic has many flaws - some of them are:
1- Since the messageCh is buffered, this code is not blocking:

    for i := 0; i < 10; i   {
        messageCh <- i
    }

so the next code is in the fast path to run:

disconnectCh <- struct{}{}

if you make the disconnectCh buffered, this line runs without blocking too, and the SelectDemo function may exit befor running the wg.Add(1).

So: You must put:

wg.Add(1)

before

go func() {

2- Even with wg.Add(1) before go func() { code -
you have:

    defer close(messageCh)
    defer close(disconnectCh)

which will close both channels at SelectDemo function return And this select is a random selection since both channels are ready:

fmt.Println("  goroutine")
        for {
            select {
            case v := <-messageCh:
                fmt.Println(v)
            case <-disconnectCh:

and it is highly likely that the second select:

                for {
                    select {
                    case v := <-messageCh:
                        fmt.Println(v)
                    default:
                        fmt.Println("  disconnection, return")
                        wg.Done()
                        return
                    }
                }

will run forever since the messageCh is closed, returning 0 forever after channel data read:

case v := <-messageCh:
    fmt.Println(v)

CodePudding user response:

Program execution is fast

Visit URL: https://pkg.go.dev/sync#WaitGroup.Add

Note that calls with a positive delta that occur when the counter is zero must happen before a Wait. Calls with a negative delta, or calls with a positive delta that start when the counter is greater than zero, may happen at any time. Typically this means the calls to Add should execute before the statement creating the goroutine or other event to be waited for. If a WaitGroup is reused to wait for several independent sets of events, new Add calls must happen after all previous Wait calls have returned. See the WaitGroup example.

func SelectDemo(wg *sync.WaitGroup) {

    messageCh := make(chan int, 10)
    disconnectCh := make(chan struct{}, 1)
    //  go routine won't run if channel is buffered
    //disconnectCh := make(chan struct{}, 1)

    wg.Add(1)

    defer close(messageCh)
    defer close(disconnectCh)
    go func() {
        fmt.Println("  goroutine")
        for {
            select {
            case v := <-messageCh:
                fmt.Println(v)
            case <-disconnectCh:
                fmt.Println("  disconnectCh")
                //  empty the buffered channel before exiting

                fmt.Println("  disconnection, return")
                wg.Done()
                return
            }
        }
    }()

    fmt.Println("Sending ints")
    for i := 0; i < 10; i   {
        messageCh <- i
    }

    //Delay sending exit signal
    time.Sleep(3 * time.Second)

    fmt.Println("Sending done")
    disconnectCh <- struct{}{}
}

I modified your code

try again!!!

  • Related