英文:
How to print N integers using odd and even thread
问题
我正在尝试从两个Go协程打印N个数字:
go协程odd(): 只能打印奇数
go协程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()
}
英文:
I am trying to print N numbers from 2 go routines:
go routine odd(): this can only print odd numbers
go routine even(): this can only print even numbers
The output should be: 1 2 3 4 5 6 7 8 9 10
I am trying to solve this problem using sync.WaitGroup. I have following queries:
Q1. Which concurrency mechanism best suited for this problem? channel, waitgroup, mutex, etc? It would be ideal if you could provide a working code for the same.
Q2. Why I am not able to print the sequence correctly through below code? I am doing something wrong which I am not able to rectify. please help in rectifying.
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()
}
答案1
得分: 2
> Q1. 哪种并发机制最适合这个问题?通道(channel)、等待组(waitgroup)、互斥锁(mutex)等?
没有。你的问题与并发相反,没有任何并发机制可以帮助你。
> Q2. 为什么我无法通过下面的代码正确打印序列?
你想要独立的 goroutine 运行同步。所以你必须打破并发。从技术上讲,这两个 goroutine 之间基于通道的乒乓球方式可以工作,实际上使它们不再并发。你的问题没有一个合理的“并发”解决方案,你不会从一个强制破坏并发的残缺解决方案中学到什么。
英文:
> Q1. Which concurrency mechanism best suited for this problem? channel, waitgroup, mutex, etc?
None. Your problem is the opposite of something to be done concurrently and no concurrency mechanism helps you.
> Q2. Why I am not able to print the sequence correctly through below code?
You want independent goroutines to run synchronised. So you have to break concurrency. Technically a channel-based ping pong between these two goroutines would work, actually rendering them unconcurrent. Your problem has no sensible "concurrent" solution and you are not going to learn something from a crippled solution where concurrency is forcefully destroyed.
答案2
得分: 1
对@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 // 这将确保我们从偶数开始
go odd(n, syncChannel, done)
<-done
close(syncChannel)
}
请注意,这是一段Go语言代码,用于展示如何使用goroutine和channel来交替打印奇数和偶数。
英文:
Some improvement on @Vibhor-Dubey-InfiniteLearner's answer, as there is a deadlock in his answer
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)
}
答案3
得分: 0
Q1: 这个问题最适合使用哪种并发机制?
A1: 没有。你的问题是顺序打印数字,并不涉及并发。因此,即使你实现了一个使用Go的并发机制(使用通道或互斥锁)的解决方案,它也不能真正并发运行,因为你想要的是按顺序打印数字。并发运行会以非确定性的顺序打印数字。
Q2: 为什么我无法通过下面的代码正确打印出序列?
A2: 你的代码打印顺序错误,因为一旦启动了goroutine,你就无法知道它们执行的顺序。所以代码:
...
go even()
go odd()
...
甚至不能保证even
函数内部的循环会在odd
函数内部的循环之前开始,尽管even
函数在之前被调用。
A2.1: 你的代码有时可能会出现恐慌WaitGroup is reused before previous Wait has returned
,因为在even
函数内部调用wgO.Done()
之前,odd
函数内部可能会调用wgO.Wait()
。
以下是一个使用sync.WaitGroup
的非常简单的实现,它可以按顺序打印数字,并说明了解决你的问题需要破坏并发性。为了按顺序打印数字,我必须等待每个goroutine完成...
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并发机制的好例子。
有一个很好的关于学习Go并发的课程,你可以在这里找到。
英文:
Q1: Which concurrency mechanism best suited for this problem?
A1: None. Your problem of printing sequential numbers is not a concurrent one. So even if you implemented a solution that uses Go's concurrent mechanisms (either with channels or mutexs), it wouldn't/couldn't actually run concurrently, since what you want is sequentially printing your numbers. Running concurrently would print numbers is a non-deterministic order.
Q2: Why I am not able to print the sequence correctly through below code?
A2: Your code prints out of order because once go routines have been triggered, you have no way of knowing the order in which they are executed. So the code:
...
go even()
go odd()
...
doesn't even guarantee that the loop inside your even
function will start before the loop inside your odd
function, even thou the even
function is called before.
A2.1: Your code can panic sometimes WaitGroup is reused before previous Wait has returned
, because it's possible for wgO.Done()
inside odd
function to be called before wgO.Wait()
is called inside the even
function.
The following is a very silly implementation using sync.WaitGroup
that works, and illustrates how the solution to your problem has to break concurrency to work. In order to print the numbers in order, I have to wait the completion of each go routine...
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()
}
}
}
I wouldn't get too caught up in this problem as it's not a good example for leaning Go's concurrency mechanism.
There is a great course on cooursera for learning Go's concurrency here
答案4
得分: 0
package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
var done int32
syncChannel := make(chan bool) // 无缓冲通道。
wg := new(sync.WaitGroup)
wg.Add(2)
go func() {
// 打印偶数。
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() {
// 打印奇数。
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/
英文:
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)
}
Reference: https://ayada.dev/snippets/ordered-printing-with-goroutines-in-go/
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论