等待 go routines 完成然后从通道读取
Wait for go routines to finish then read from channel
如何等待所有 go 例程完成,然后从通道读取所有数据?
为什么此示例在等待 go 例程完成时卡住?
package main
import (
"fmt"
"sync"
"time"
)
func doStuff(i int, wg *sync.WaitGroup, messages chan<- string) {
defer wg.Done()
time.Sleep(time.Duration(i) * time.Second)
messages <- fmt.Sprintf("Doing stuff...%d", i)
}
func doMoreStuff(i int, wg *sync.WaitGroup, messages chan<- string) {
defer wg.Done()
time.Sleep(time.Duration(i) * time.Second)
messages <- fmt.Sprintf("Doing more stuff...%d", i)
}
func main() {
var wg sync.WaitGroup
var messages = make(chan string)
for start := time.Now();; {
elapsedTime := time.Since(start)
fmt.Println(elapsedTime)
if elapsedTime > time.Duration(3) * time.Second {
fmt.Println("BREAK")
break
}
for i := 0; i < 10; i++ {
wg.Add(1)
go doStuff(i, &wg, messages)
wg.Add(1)
go doMoreStuff(i, &wg, messages)
}
time.Sleep(time.Duration(1) * time.Second)
}
fmt.Println("WAITING")
wg.Wait()
fmt.Println("DONE")
for message := range messages {
fmt.Println(message)
}
}
如果你想等待所有 goroutines 完成在通道上发送消息,然后你想开始读取消息,那么你别无选择,只能使用缓冲通道 "host" goroutines 可以发送的所有消息。
这不实用。即使你沿着这条路走下去,你也能够接收和打印消息,但是这样做的 for range
循环永远不会终止,因为它只会在收到通道上发送的所有消息时终止在它关闭之前,但你永远不会关闭通道。您可以在 Go Playground.
上查看此 "half-working" 解决方案
改为启动等待其他人完成的 goroutine,然后关闭通道:
go func() {
fmt.Println("WAITING")
wg.Wait()
close(messages)
}()
所以现在你可以使用 for range
在 main
goroutine 中接收消息:
for message := range messages {
fmt.Println(message)
}
fmt.Println("DONE")
在 Go Playground 上试试这个。
这个解决方案仍然不完美:它首先必须启动所有 goroutines,然后才尝试接收值,所有启动的 goroutines 将被阻止发送操作(直到 main
准备就绪收到那些)。
更好的解决方案可能是启动一个 goroutine 来接收值,最好是在 goroutines 在通道上发送消息之前启动(否则它们无论如何都会在发送时被阻止):
go func() {
for message := range messages {
fmt.Println(message)
}
}()
并等待 goroutines 并在 main()
结束时关闭通道:
fmt.Println("WAITING")
wg.Wait()
close(messages)
问题在于,当 main()
结束时,您的应用程序也会结束,它不会等待其他非 main
goroutines 完成。这意味着它不会等待 "consumer" goroutine 接收消息。
要等待 "consumer",您可以使用额外的 sync.WaitGroup
:
var wg2 sync.WaitGroup
wg2.Add(1)
go func() {
defer wg2.Done()
for message := range messages {
fmt.Println(message)
}
}()
// And the end of `main()`:
fmt.Println("WAITING")
wg.Wait()
close(messages)
fmt.Println("DONE")
wg2.Wait()
在 Go Playground 上试试这个。
示例卡住,因为您使用的是无缓冲通道。两个 go-routines 都被阻塞等待写入通道,因为没有任何东西可以从它读取。
您可以使用缓冲通道,但您只能将其用作数据存储。渠道对于沟通更有用。问题是为什么要等到所有写手都写完了?在未知(无限)数量的作者的一般情况下,您将不知道要使您的频道有多大。
如何等待所有 go 例程完成,然后从通道读取所有数据?
为什么此示例在等待 go 例程完成时卡住?
package main
import (
"fmt"
"sync"
"time"
)
func doStuff(i int, wg *sync.WaitGroup, messages chan<- string) {
defer wg.Done()
time.Sleep(time.Duration(i) * time.Second)
messages <- fmt.Sprintf("Doing stuff...%d", i)
}
func doMoreStuff(i int, wg *sync.WaitGroup, messages chan<- string) {
defer wg.Done()
time.Sleep(time.Duration(i) * time.Second)
messages <- fmt.Sprintf("Doing more stuff...%d", i)
}
func main() {
var wg sync.WaitGroup
var messages = make(chan string)
for start := time.Now();; {
elapsedTime := time.Since(start)
fmt.Println(elapsedTime)
if elapsedTime > time.Duration(3) * time.Second {
fmt.Println("BREAK")
break
}
for i := 0; i < 10; i++ {
wg.Add(1)
go doStuff(i, &wg, messages)
wg.Add(1)
go doMoreStuff(i, &wg, messages)
}
time.Sleep(time.Duration(1) * time.Second)
}
fmt.Println("WAITING")
wg.Wait()
fmt.Println("DONE")
for message := range messages {
fmt.Println(message)
}
}
如果你想等待所有 goroutines 完成在通道上发送消息,然后你想开始读取消息,那么你别无选择,只能使用缓冲通道 "host" goroutines 可以发送的所有消息。
这不实用。即使你沿着这条路走下去,你也能够接收和打印消息,但是这样做的 for range
循环永远不会终止,因为它只会在收到通道上发送的所有消息时终止在它关闭之前,但你永远不会关闭通道。您可以在 Go Playground.
改为启动等待其他人完成的 goroutine,然后关闭通道:
go func() {
fmt.Println("WAITING")
wg.Wait()
close(messages)
}()
所以现在你可以使用 for range
在 main
goroutine 中接收消息:
for message := range messages {
fmt.Println(message)
}
fmt.Println("DONE")
在 Go Playground 上试试这个。
这个解决方案仍然不完美:它首先必须启动所有 goroutines,然后才尝试接收值,所有启动的 goroutines 将被阻止发送操作(直到 main
准备就绪收到那些)。
更好的解决方案可能是启动一个 goroutine 来接收值,最好是在 goroutines 在通道上发送消息之前启动(否则它们无论如何都会在发送时被阻止):
go func() {
for message := range messages {
fmt.Println(message)
}
}()
并等待 goroutines 并在 main()
结束时关闭通道:
fmt.Println("WAITING")
wg.Wait()
close(messages)
问题在于,当 main()
结束时,您的应用程序也会结束,它不会等待其他非 main
goroutines 完成。这意味着它不会等待 "consumer" goroutine 接收消息。
要等待 "consumer",您可以使用额外的 sync.WaitGroup
:
var wg2 sync.WaitGroup
wg2.Add(1)
go func() {
defer wg2.Done()
for message := range messages {
fmt.Println(message)
}
}()
// And the end of `main()`:
fmt.Println("WAITING")
wg.Wait()
close(messages)
fmt.Println("DONE")
wg2.Wait()
在 Go Playground 上试试这个。
示例卡住,因为您使用的是无缓冲通道。两个 go-routines 都被阻塞等待写入通道,因为没有任何东西可以从它读取。
您可以使用缓冲通道,但您只能将其用作数据存储。渠道对于沟通更有用。问题是为什么要等到所有写手都写完了?在未知(无限)数量的作者的一般情况下,您将不知道要使您的频道有多大。