如何使用奇偶线程打印N个整数

huangapple go评论81阅读模式
英文:

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 (
    &quot;fmt&quot;
    &quot;sync&quot;
)

var wg sync.WaitGroup
var wgO sync.WaitGroup
var wgE sync.WaitGroup

func even() {
    defer wg.Done()

    for i := 2; i &lt;= 10; i += 2 {
	    wgE.Add(1)
	    wgO.Wait()
	    fmt.Println(i)
	    wgE.Done()
    }
}
func odd() {
    defer wg.Done()

    for i := 1; i &lt;= 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 &lt; n; i++ {
		&lt;-syncChannel

		if i%2 != 0 {
			fmt.Println(i)
		}

		syncChannel &lt;- true
	}
	done &lt;- true
}

func even(n int, syncChannel, done chan bool) {

	for i := 0; i &lt; n; i++ {
		&lt;-syncChannel

		if i%2 == 0 {
			fmt.Println(i)
		}

		syncChannel &lt;- true
	}

	done &lt;- true
}

func main() {
	done := make(chan bool)
	syncChannel := make(chan bool)
	n := 10

	go even(n, syncChannel, done)
	syncChannel &lt;- true // This will ensure, we start at even number
	go odd(n, syncChannel, done)

	&lt;-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 &lt;= 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 (
	&quot;fmt&quot;
	&quot;sync&quot;
	&quot;sync/atomic&quot;
)

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 &lt; 50; i += 2 {
			&lt;-syncChannel

			fmt.Printf(&quot;Even: %d\n&quot;, i)

			syncChannel &lt;- true
		}
		atomic.StoreInt32(&amp;done, 1)
	}()

	syncChannel &lt;- true

	go func() {
		// prints odd numbers.
		defer wg.Done()

		for i := 1; i &lt; 50; i += 2 {
			&lt;-syncChannel

			fmt.Printf(&quot;Odd:%d\n&quot;, i)

			if atomic.LoadInt32(&amp;done) != 0 {
				return
			}

			syncChannel &lt;- true
		}
	}()

	wg.Wait()
	close(syncChannel)
}

Reference: https://ayada.dev/snippets/ordered-printing-with-goroutines-in-go/

huangapple
  • 本文由 发表于 2021年8月12日 23:09:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/68759997.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定