从 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 := <- c
是否阻塞worker
?我不这么认为,因为通道是缓冲的。
如果它是阻塞的,我如何使它成为非阻塞的?我的要求是让worker
及其调用者继续其余的工作,而不是等待值出现在频道上。
谢谢。
【问题讨论】:
如果您还没有准备好等待接收来自c
的信息,请不要这样做。你是把rest of the work
放在<-c
之后的那个人;只需交换订单
@DanielFarrell,worker()
是否会被阻止,直到通道上出现值 c
?
@Someone 是的。 有些东西必须等待它(否则不要费心发送它)。
是的,一旦您从c
读取数据,您就会阻止c
上的数据准备就绪。这就是你想要的:你想确保 both 协程与c
“同步”,因为一旦你从c
收到,你假设helper
在功能上是完整的。这里的每个人都在说,首先启动helper
,然后让worker
完成它的工作,然后等待c
这样你就知道了helper
完成了它的工作,然后你就知道所有工作都完成了。
@DanielFarrell,是的,我明白了。但我的要求是helper
和worker
都独立执行,如worker
(及其调用者等)都应该继续工作,而不会阻塞helper
。 helper
将在完成其工作时返回 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 := <- c
将阻止工作人员。
问:如果它是阻塞的,我如何使它成为非阻塞的?我的要求是让 worker 及其调用者继续其余的工作,而不是等待值出现在通道上。
答:如果您不想屏蔽,只需删除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
或者你可以直接使用它,去你的函数,然后在你想要的任何地方等待错误。
【讨论】:
【参考方案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()
。它将错误/零返回给它的调用者。那么,这个操作会被阻止吗?
是的,该特定操作将阻塞,直到助手向通道发送 error
或 nil
。但是工人只有在完成所有工作后才会被阻止。
但这会阻止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 通道读取而不阻塞的主要内容,如果未能解决你的问题,请参考以下文章