Home > Enterprise >  Reading from a goroutine channel without blocking
Reading from a goroutine channel without blocking

Time:11-24

I have two goroutines: the main worker and a helper that it spins off for some help. helper can encounter errors, so I use a channel to communicate errors over from the helper to the worker.

func helper(c chan <- error) (){
    //do some work
    c <- err // send errors/nil on c
}

Here is how helper() is called:

func worker() error {
    //do some work
    c := make(chan error, 1)
    go helper(c)
    err := <- c
    return err
}

Questions:

  • Is the statement err := <- c blocking worker? I don't think so, since the channel is buffered.

  • If it is blocking, how do I make it non-blocking? My requirement is to have worker and its caller continue with rest of the work, without waiting for the value to appear on the channel.

Thanks.

CodePudding user response:

In your code, the rest of the work is independent of whether the helper encountered an error. You can simply receive from the channel after the rest of the work is completed.

func worker() error {
    //do some work
    c := make(chan error, 1)
    go helper(c)
    //do rest of the work
    return <-c
}

CodePudding user response:

You can easily verify

func helper(c chan<- error) {
    time.Sleep(5 * time.Second)
    c <- errors.New("") // send errors/nil on c
}

func worker() error {
    fmt.Println("do one")

    c := make(chan error, 1)
    go helper(c)

    err := <-c
    fmt.Println("do two")

    return err
}

func main() {
    worker()
}

Q: Is the statement err := <- c blocking worker? I don't think so, since the channel is buffered.

A: err := <- c will block worker.

Q: If it is blocking, how do I make it non-blocking? My requirement is to have worker and its caller continue with rest of the work, without waiting for the value to appear on the channel.

A: If you don't want blocking, just remove err := <-c. If you need err at the end, just move err := <-c to the end.

You can not read channel without blocking, if you go through without blocking, can can no more exec this code, unless your code is in a loop.

Loop:
    for {
        select {
        case <-c:
            break Loop
        default:
            //defalut will go through without blocking
        }
        // do something
    }

And have you ever seen errgroup or waitgroup?

It use atomic, cancel context and sync.Once to implement this.

https://github.com/golang/sync/blob/master/errgroup/errgroup.go

https://github.com/golang/go/blob/master/src/sync/waitgroup.go

Or you can just use it, go you func and then wait for error in any place you want.

  • Related