从 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
}
问题:
语句err := <- c
是否阻塞了worker
?我不这么认为,因为频道是缓冲的。
如果是阻塞的,如何让它变成非阻塞的?我的要求是让 worker
及其调用者继续完成其余工作,而无需 等待 值出现在通道上。
谢谢。
在您的代码中,其余工作与助手是否遇到错误无关。剩下的工作完成后,您可以简单地从频道接收。
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()
}
我有两个 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
}
问题:
语句
err := <- c
是否阻塞了worker
?我不这么认为,因为频道是缓冲的。如果是阻塞的,如何让它变成非阻塞的?我的要求是让
worker
及其调用者继续完成其余工作,而无需 等待 值出现在通道上。
谢谢。
在您的代码中,其余工作与助手是否遇到错误无关。剩下的工作完成后,您可以简单地从频道接收。
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()
}