如何正确使用通道来控制并发?



我是Go中的并发新手,我正在尝试弄清楚如何使用通道来控制并发性。 我想做的是一个循环,我可以使用新的 go 例程调用一个函数,并在该函数处理时继续循环,我想将运行的例程数量限制为 3。 我第一次尝试这样做是下面的代码:

func write(val int, ch chan bool) {
fmt.Println("Processing:", val)
time.Sleep(2 * time.Second)
ch <- val % 3 == 0
}
func main() {
ch := make(chan bool, 3) // limit to 3 routines?
for i := 0; i< 10; i++ {
go write(i, ch)
resp := <- ch
fmt.Println("Divisible by 3:", resp)
}
time.Sleep(20 * time.Second)
}

我的印象是,这基本上会一次调用write3,然后推迟处理下一个 3,直到前 3 个完成。 根据日志记录的内容,它似乎一次只处理一个。 代码可以在这里找到并执行。

在此示例中,我需要更改哪些内容才能获得上面描述的功能?

这里的问题很简单:

for i := 0; i< 10; i++ {
go write(i, ch)
resp := <- ch
fmt.Println("Divisible by 3:", resp)
}

你启动一个goroutine,然后等待它响应,然后你继续循环并启动下一个goroutine。它们不能并行运行,因为您永远不会同时运行其中两个。

要解决此问题,您需要启动所有 10 个 goroutine,然后等待所有 10 个响应(操场(:

for i := 0; i< 10; i++ {
go write(i, ch)
}
for i := 0; i<10; i++ {
resp := <- ch
fmt.Println("Divisible by 3:", resp)
}

现在你在通道上确实有 7 个 goroutines 阻塞——但它太短了,你看不到它发生,所以输出不会很有趣。如果您尝试在 goroutine 结束时添加一条Processed消息,并在每个通道读取之间休眠,您将看到其中 3 个立即完成(好吧,在等待 2 秒后(,然后其他消息解锁并逐个完成(操场(。

还有另一种并行运行go例程的方法,等待它们全部返回通道上的值,即使用Wait groups。它还有助于同步 go 例程。如果您正在使用 go 例程以等待所有例程完成,然后再执行另一个函数,更好的方法是使用wait group

package main
import (
"fmt"
"time"
"sync"
)
func write(val int, wg *sync.WaitGroup, ch chan bool) {
defer wg.Done()
fmt.Println("Processing:", val)
time.Sleep(2 * time.Second)
ch <- val % 3 == 0
}
func main() {
wg := &sync.WaitGroup{}
ch := make(chan bool, 3)
for i := 0; i< 10; i++ {
wg.Add(1)
go write(i, wg, ch)
}
for i := 0; i< 10; i++ {
fmt.Println("Divisible by 3: ", <-ch)
}
close(ch)
wg.Wait()
time.Sleep(20 * time.Second)
}

游乐场示例

最新更新