从 goroutine 通道读取而不阻塞

Posted

技术标签:

【中文标题】从 goroutine 通道读取而不阻塞【英文标题】:Reading from a goroutine channel without blocking 【发布时间】:2022-01-02 11:32:48 【问题描述】:

我有两个 goroutine:主要的 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

问题:

语句err := &lt;- c 是否阻塞worker?我不这么认为,因为通道是缓冲的。

如果它是阻塞的,我如何使它成为非阻塞的?我的要求是让worker 及其调用者继续其余的工作,而不是等待值出现在频道上。

谢谢。

【问题讨论】:

如果您还没有准备好等待接收来自c 的信息,请不要这样做。你是把rest of the work放在&lt;-c之后的那个人;只需交换订单 @DanielFarrell,worker() 是否会被阻止,直到通道上出现值 c @Someone 是的。 有些东西必须等待它(否则不要费心发送它)。 是的,一旦您从c 读取数据,您就会阻止c 上的数据准备就绪。这就是你想要的:你想确保 both 协程与c“同步”,因为一旦你从c 收到,你假设helper 在功能上是完整的。这里的每个人都在说,首先启动helper然后worker完成它的工作,然后等待c这样你就知道了helper完成了它的工作,然后你就知道所有工作都完成了。 @DanielFarrell,是的,我明白了。但我的要求是helperworker 都独立执行,如worker(及其调用者等)都应该继续工作,而不会阻塞helperhelper 将在完成其工作时返回 error/nil。这有意义吗? 【参考方案1】:

您可以轻松验证

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


问:语句 err :=

答: err := &lt;- c 将阻止工作人员。

问:如果它是阻塞的,我如何使它成为非阻塞的?我的要求是让 worker 及其调用者继续其余的工作,而不是等待值出现在通道上。

答:如果您不想屏蔽,只需删除err := &lt;-c。如果最后需要 err,只需将 err := &lt;-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

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

【讨论】:

【参考方案2】:

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

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

【讨论】:

那么,在c 上出现值之前,不会阻塞worker() 吗? 另外,我刚刚编辑了worker()。它将错误/零返回给它的调用者。那么,这个操作会被阻止吗? 是的,该特定操作将阻塞,直到助手向通道发送 errornil。但是工人只有在完成所有工作后才会被阻止。 但这会阻止worker 的调用者。有没有办法让它不阻塞? 如果工作者及其调用者不等待助手完成,它如何从助手返回错误?【参考方案3】:

我认为您需要此代码..

运行这段代码

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

【讨论】:

您能否通过对此答案的编辑来解释为什么这会有所帮助?我想知道这对问题作者和未来的读者是否有用。

以上是关于从 goroutine 通道读取而不阻塞的主要内容,如果未能解决你的问题,请参考以下文章

与通道和 goroutine 同步

使用带有选择的通道时的 Goroutine 死锁

如何阻止从UDP读取的goroutine?

每天一个知识点:Go 语言当中 Channel(通道)有啥特点,需要注意啥?

从标准输入读取而不阻塞

单个通道上的多个接收器。谁得到数据?