卡在 golang 中的频道
Stuck with Channels in golang
我需要运行一个函数并行执行多次。
如果即使函数 returns true
(在频道上发送 true
),那么最终结果应该是 true
.
如何使用 goroutines 和通道实现此目的?
// Some performance intensive function
func foo(i int, c chan bool) {
// do some processing and return either true or false
c <- true // or false
}
func main() {
flg := false
ch := make(chan bool)
for i := 0; i < 10; i++ {
go foo(i, ch)
}
// If even once foo() returned true then val should be true
flg = flg || <-ch
}
您可以从 ch
频道开始阅读,并在获得真实结果后将 flg
设置为 true
。像这样:
//flg = flg || <- ch
for res := range ch {
if res {
flg = true
}
}
这种方法可行,但有一个严重的缺点 - for
循环无限等待来自通道的新值。停止循环的惯用方法是关闭通道。您可以这样做:运行 一个单独的 goroutine,它将等待所有 goroutine 退出。 Go 提供了一个非常方便的工具来做到这一点 - sync.WaitGroup
.
在全局范围内定义它,以便每个 goroutine 都可以访问它:
var (
wg sync.WaitGroup
)
然后每次启动 goroutine 时,都会再添加一个 goroutine 到等待组:
for i := 0; i < 10; i++ {
wg.Add(1) // here
go foo(i, ch)
}
当 goroutine 完成时它调用 wg.Done
方法来标记它。
func foo(i int, c chan bool) {
//do some processing and return either true or false
c <- true //or false
wg.Done() // here
}
然后 sepatate goroutine 等待所有 foo goroutines 退出并关闭通道。 wg.Wait
块,直到全部完成:
go func() {
wg.Wait()
close(ch)
}()
您只从通道接收到一个值(这将是 foo()
调用之一发送的值,无法预测是哪一个),但您想要接收所有。
所以使用一个for
循环来接收你在其上发送(发送)的值:
for i := 0; i < 10; i++ {
flg = flg || <-ch
}
虽然在你的情况下循环直到收到一个 true
值就足够了,因为这将决定 flg
的最终值,但仍然建议接收所有其他值剩余的 goroutines 将被阻塞(因为 ch
是一个无缓冲的通道)。在此示例中,这无关紧要,但在 "real-life" 应用程序中,它会导致 goroutines 永远卡住(内存泄漏)。
如果您不想等待所有 foo()
调用完成并尽快 return(一旦遇到一个 true
值),一个选项是让 ch
缓冲,所以所有的 goroutines 都可以在它上面发送值而不会被阻塞。这样你就不需要接收(并因此等待)所有 foo()
调用来完成:
ch := make(chan bool, 10)
for i := 0; i < 10; i++ {
go foo(i, ch)
}
flg := false
for i := 0; i < 10; i++ {
if <-ch {
flg = true
break
}
}
选择这种方法,您应该提供取消不再需要工作的 goroutine 的方法,以避免不必要的 CPU(和内存)使用。 context.Context
is such a mean, read more about it here: .
我需要运行一个函数并行执行多次。
如果即使函数 returns true
(在频道上发送 true
),那么最终结果应该是 true
.
如何使用 goroutines 和通道实现此目的?
// Some performance intensive function
func foo(i int, c chan bool) {
// do some processing and return either true or false
c <- true // or false
}
func main() {
flg := false
ch := make(chan bool)
for i := 0; i < 10; i++ {
go foo(i, ch)
}
// If even once foo() returned true then val should be true
flg = flg || <-ch
}
您可以从 ch
频道开始阅读,并在获得真实结果后将 flg
设置为 true
。像这样:
//flg = flg || <- ch
for res := range ch {
if res {
flg = true
}
}
这种方法可行,但有一个严重的缺点 - for
循环无限等待来自通道的新值。停止循环的惯用方法是关闭通道。您可以这样做:运行 一个单独的 goroutine,它将等待所有 goroutine 退出。 Go 提供了一个非常方便的工具来做到这一点 - sync.WaitGroup
.
在全局范围内定义它,以便每个 goroutine 都可以访问它:
var (
wg sync.WaitGroup
)
然后每次启动 goroutine 时,都会再添加一个 goroutine 到等待组:
for i := 0; i < 10; i++ {
wg.Add(1) // here
go foo(i, ch)
}
当 goroutine 完成时它调用 wg.Done
方法来标记它。
func foo(i int, c chan bool) {
//do some processing and return either true or false
c <- true //or false
wg.Done() // here
}
然后 sepatate goroutine 等待所有 foo goroutines 退出并关闭通道。 wg.Wait
块,直到全部完成:
go func() {
wg.Wait()
close(ch)
}()
您只从通道接收到一个值(这将是 foo()
调用之一发送的值,无法预测是哪一个),但您想要接收所有。
所以使用一个for
循环来接收你在其上发送(发送)的值:
for i := 0; i < 10; i++ {
flg = flg || <-ch
}
虽然在你的情况下循环直到收到一个 true
值就足够了,因为这将决定 flg
的最终值,但仍然建议接收所有其他值剩余的 goroutines 将被阻塞(因为 ch
是一个无缓冲的通道)。在此示例中,这无关紧要,但在 "real-life" 应用程序中,它会导致 goroutines 永远卡住(内存泄漏)。
如果您不想等待所有 foo()
调用完成并尽快 return(一旦遇到一个 true
值),一个选项是让 ch
缓冲,所以所有的 goroutines 都可以在它上面发送值而不会被阻塞。这样你就不需要接收(并因此等待)所有 foo()
调用来完成:
ch := make(chan bool, 10)
for i := 0; i < 10; i++ {
go foo(i, ch)
}
flg := false
for i := 0; i < 10; i++ {
if <-ch {
flg = true
break
}
}
选择这种方法,您应该提供取消不再需要工作的 goroutine 的方法,以避免不必要的 CPU(和内存)使用。 context.Context
is such a mean, read more about it here: