从 goroutine 通道读取而不阻塞

Reading from a goroutine channel without blocking

我有两个 goroutines:主要的 worker 和一个 helper,它为了一些帮助而分离出来。 helper 可能会遇到错误,所以我使用一个通道将错误从 helper 传递到 worker

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

下面是 helper() 的调用方式:

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

问题:

谢谢。

在您的代码中,其余工作与助手是否遇到错误无关。剩下的工作完成后,您可以简单地从频道接收。

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

您可以轻松验证

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 会阻塞 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:不想屏蔽就去掉err := <-c。如果最后需要err,直接把err := <-c移到最后即可。

你不能不阻塞地读取通道,如果你不阻塞地通过,就不能再执行这段代码,除非你的代码在循环中。

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

你见过errgroup或waitgroup吗?

它使用原子、取消上下文和 sync.Once 来实现它。

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

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

或者你可以直接使用它,go you func 然后在你想要的任何地方等待错误。

我想你需要这段代码..

运行 这个代码

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()
}