我正在尝试从 2 个 go 例程中打印 N 个数字:
go routine odd(): 这个只能打印奇数
go routine even(): 这只能打印偶数
输出应该是: 1 2 3 4 5 6 7 8 9 10
我正在尝试使用 sync.WaitGroup 来解决这个问题。我有以下疑问:
Q1。哪种并发机制最适合这个问题?通道、等待组、互斥量等?如果您能提供相同的工作代码,那将是理想的。
Q2。为什么我无法通过以下代码正确打印序列?我做错了我无法纠正的事情。请帮助纠正。
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
var wgO sync.WaitGroup
var wgE sync.WaitGroup
func even() {
defer wg.Done()
for i := 2; i <= 10; i += 2 {
wgE.Add(1)
wgO.Wait()
fmt.Println(i)
wgE.Done()
}
}
func odd() {
defer wg.Done()
for i := 1; i <= 10; i += 2 {
wgO.Add(1)
fmt.Println(i)
wgO.Done()
wgE.Wait()
}
}
func main() {
wg.Add(2)
go even()
go odd()
wg.Wait()
}
Q1。哪种并发机制最适合这个问题?通道、等待组、互斥量等?
无。您的问题与要同时完成的事情相反,没有并发机制可以帮助您。
Q2。为什么我无法通过以下代码正确打印序列?
您希望独立的 goroutines 同步运行。所以你必须打破并发。从技术上讲,这两个 goroutine 之间基于通道的乒乓球会起作用,实际上使它们不并发。你的问题没有明智的“并发”解决方案,你不会从并发性被强行破坏的残缺解决方案中学到东西。
对@Vibhor-Dubey-InfiniteLearner 的回答进行了一些改进,因为他的回答出现了僵局
func odd(n int, syncChannel, done chan bool) {
for i := 0; i < n; i++ {
<-syncChannel
if i%2 != 0 {
fmt.Println(i)
}
syncChannel <- true
}
done <- true
}
func even(n int, syncChannel, done chan bool) {
for i := 0; i < n; i++ {
<-syncChannel
if i%2 == 0 {
fmt.Println(i)
}
syncChannel <- true
}
done <- true
}
func main() {
done := make(chan bool)
syncChannel := make(chan bool)
n := 10
go even(n, syncChannel, done)
syncChannel <- true // This will ensure, we start at even number
go odd(n, syncChannel, done)
<-done
close(syncChannel)
}
Q1:
Which concurrency mechanism best suited for this problem?
A1:没有。您打印序号的问题不是并发的。因此,即使您实现了一个使用 Go 的并发机制(使用通道或互斥锁)的解决方案,它实际上也不会/不能并发运行,因为您想要的是 sequentially 打印您的数字。同时运行将打印数字是一个不确定的顺序。
Q2:
Why I am not able to print the sequence correctly through below code?
A2: 你的代码打印顺序不对 因为一旦 go 例程被触发,你就无法知道它们的执行顺序。所以代码:
...
go even()
go odd()
...
甚至不能保证您的
even
函数内的循环将在您的odd
函数内的循环之前开始,即使您之前调用了even
函数。
A2.1: 您的代码有时会出现 panic
WaitGroup is reused before previous Wait has returned
,因为 wgO.Done()
在 odd
函数中被调用之前 wgO.Wait()
在 even
函数中调用是可能的
silly 使用 sync.WaitGroup
的实现,它说明了您的问题的解决方案如何必须打破并发才能工作。为了按顺序打印数字,我必须等待每个 go 例程的完成...
func main() {
var wg sync.WaitGroup
for i := 0; i <= 10; i++ {
if i%2 == 0 {
wg.Add(1)
go func(i int) {
defer wg.Done()
fmt.Println(i)
}(i)
wg.Wait()
} else {
wg.Add(1)
go func(i int) {
defer wg.Done()
fmt.Println(i)
}(i)
wg.Wait()
}
}
}
我不会太在意这个问题,因为它不是学习 Go 并发机制的好例子。在 cooursera 上有一个很好的学习 Go 并发的课程
package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
var done int32
syncChannel := make(chan bool) // unbuffered channel.
wg := new(sync.WaitGroup)
wg.Add(2)
go func() {
// prints even numbers.
defer wg.Done()
for i := 0; i < 50; i += 2 {
<-syncChannel
fmt.Printf("Even: %d\n", i)
syncChannel <- true
}
atomic.StoreInt32(&done, 1)
}()
syncChannel <- true
go func() {
// prints odd numbers.
defer wg.Done()
for i := 1; i < 50; i += 2 {
<-syncChannel
fmt.Printf("Odd:%d\n", i)
if atomic.LoadInt32(&done) != 0 {
return
}
syncChannel <- true
}
}()
wg.Wait()
close(syncChannel)
}
参考:https://ayada.dev/snippets/ordered-printing-with-goroutines-in-go/