Reading from a goroutine channel without blocking

0

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.

channel go goroutine
2021-11-24 01:59:57
3

2

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:
            //default 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.

2021-12-01 21:31:34
1

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
}
2021-11-24 02:54:28

Well, wouldn't worker() be blocked until a value appears on c?
Someone

Also, I just edited worker(). It returns the error/nil to its caller. So, would this operation be blocked?
Someone

Yes that particular operation will block until helper sends an error or nil to the channel. But worker is blocked only after it has completed all its work.
Chandra Sekar

But that blocks the caller of worker. Is there a way to make it non-blocking?
Someone

If worker, and consequently its caller, does no wait for helper to complete, how can it return the error from helper?
Chandra Sekar
0

I think you need this code..

run this code

package main

import (
    "log"
    "sync"
)

func helper(c chan<- error) {

    for {
        var err error = nil
        // do job

        if err != nil {
            c <- err // send errors/nil on c
            break
        }
    }

}

func worker(c chan error) error {
    log.Println("first log")

    go func() {
        helper(c)
    }()

    count := 1
    Loop:
        for {
            select {
            case err := <- c :
                return err
            default:
                log.Println(count, " log")
                count++
                isFinished := false
                // do your job
                if isFinished {
                    break Loop // remove this when you test

                }
            }
        }
    return nil
}

func main() {
    wg := sync.WaitGroup{}
    wg.Add(1)
    go func() {
        c := make(chan error, 1)
        worker(c)
        wg.Done()
    }()
    wg.Wait()
}
2021-11-24 02:35:53

Can you explain, by way of an edit to this answer, why this would help? I wonder if that will be useful, if not to the question author, to future readers.
halfer

In other languages

This page is in other languages

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................