协程交替打印奇数和偶数的goroutine陷入了死锁。

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

goroutine to print odd and even number alternatively is running into deadlock

问题

我目前正在学习golang。我想检查一下golang的通道是如何工作的。我创建了一个程序,其中两个goroutine会交替打印奇数和偶数。尽管程序打印得很正确,但在最后却显示了一个死锁错误。从错误消息中无法清楚地看出是什么导致了这个问题。

谢谢大家的回复。我编写了以下代码来解决这个问题。

func main() {
    even := make(chan bool)
    odd := make(chan bool)
    done := make(chan bool)
    //var wait sync.WaitGroup
    //wait.Add(2)
    go func() {
        for i := 0; i <= 10; i += 2 {
            <-even
            print("Even ====>")
            println(i)
            odd <- true
        }
        close(odd)
        close(even)
        done <- true
        // wait.Done()
    }()
    go func() {
        for i := 1; ; i += 2 {
            _, ok := <-odd
            if !ok {
                //wait.Done()
                return
            }
            print("Odd ====>")
            println(i)
            select {
            case even <- true:
            case <-done:
                return
            }
        }
    }()
    even <- true
    //wait.Wait()
    <-done
}
英文:

I am currently learning golang. I wanted to check how golang channels work. I have created a program where two goroutines will print odd and even numbers alternatively. Even though the program is printing correctly, it is showing a deadlock error at the end. It is not clear from error message what is causing this issue.

func main() {
	even := make(chan bool)
	odd := make(chan bool)
	go func() {
		defer close(odd)
		for i := 0; i &lt;= 10; i += 2 {
			&lt;-even
			print(&quot;Even ====&gt;&quot;)
			println(i)
			odd &lt;- true
		}
	}()
	var wait sync.WaitGroup
	wait.Add(1)
	go func() {
		for i := 1; i &lt;= 10; i += 2 {
			_, ok := &lt;-odd
			if !ok {
				wait.Done()
				return
			}
			print(&quot;Odd ====&gt;&quot;)
			println(i)
			even &lt;- true
		}
	}()
	even &lt;- true
	wait.Wait()
}

[Edit]
Thanks everyone for your reply. I wrote the following code to solve the problem.

func main() {
	even := make(chan bool)
	odd := make(chan bool)
	done := make(chan bool)
	//var wait sync.WaitGroup
	//wait.Add(2)
	go func() {
		for i := 0; i &lt;= 10; i += 2 {
			&lt;-even
			print(&quot;Even ====&gt;&quot;)
			println(i)
			odd &lt;- true
		}
		close(odd)
		close(even)
		done &lt;- true
		// wait.Done()
	}()
	go func() {
		for i := 1; ; i += 2 {
			_, ok := &lt;-odd
			if !ok {
				//wait.Done()
				return
			}
			print(&quot;Odd ====&gt;&quot;)
			println(i)
			select {
			case even &lt;- true:
			case &lt;-done:
				return
			}
		}
	}()
	even &lt;- true
	//wait.Wait()
	&lt;-done
}




</details>


# 答案1
**得分**: 2

以下是未来读者的代码:

```go
package main

import "sync"

func main() {
	even := make(chan bool)
	odd := make(chan bool)
	go func() {
		defer close(odd)
		for i := 0; i <= 10; i += 2 {
			<-even
			print("Even ====>")
			println(i)
			odd <- true
		}
	}()
	var wait sync.WaitGroup
	wait.Add(1)
	go func() {
		for i := 1; i <= 10; i += 2 {
			_, ok := <-odd
			if !ok {
				wait.Done()
				return
			}
			print("Odd ====>")
			println(i)
			even <- true
		}
	}()
	even <- true
	wait.Wait()
}

死锁很可能很容易发现,它发生在这里 odd <- true,因为向一个无缓冲(或已满)的通道写入数据。在第二个 goroutine 的最后一次迭代中,使用了这个循环 for i := 1; i <= 10; i += 2 {,通道 odd 已经被第一个 goroutine 写入,但第二个 goroutine 无法读取它,因为迭代条件不再匹配,因为在特定的迭代中 i + 2 = 11,而 11 > 10,导致迭代停止并且通道保持填充状态。

现在,另一个 goroutine 尝试向 odd 通道写入数据,结果被阻塞,并且永远被阻塞,但由于运行时可以轻松检测到这一点...之后很快就会产生死锁。

英文:

Just in case you don't end up linking your code, here it is for future readers

package main

import &quot;sync&quot;

func main() {
	even := make(chan bool)
	odd := make(chan bool)
	go func() {
		defer close(odd)
		for i := 0; i &lt;= 10; i += 2 {
			&lt;-even
			print(&quot;Even ====&gt;&quot;)
			println(i)
			odd &lt;- true
		}
	}()
	var wait sync.WaitGroup
	wait.Add(1)
	go func() {
		for i := 1; i &lt;= 10; i += 2 {
			_, ok := &lt;-odd
			if !ok {
				wait.Done()
				return
			}
			print(&quot;Odd ====&gt;&quot;)
			println(i)
			even &lt;- true
		}
	}()
	even &lt;- true
	wait.Wait()
}

The deadlock is likely quite easy to figure that it occurs here odd &lt;- true as a result of a write into an unbuffered (or filled up) channel.
On the last iteration of your second goroutine having this loop for i := 1; i &lt;= 10; i += 2 {, the channel odd has been written into already by the first goroutine but the second goroutine can't read it because the iteration condition no longer matches because i + 2 = 11 at that particular iteration and 11 &gt; 10 which causes the iteration to stop and the channel to stay filled.

Now, the other goroutine trying to write into the odd channel gets blocked as a result and stays blocked forever but since the runtime can easily detect this...it yields a deadlock pretty quickly afterward.

答案2

得分: 2

问题可以通过以下方式解决:

  1. 删除第二个gofunc的上限(第20行)
  2. 在写入even时使用select(第28行)
英文:

The problem can be solved by

  1. Removing the for upper bound for the second gofunc (line 20)
  2. selecting when writing to even (line 28)

huangapple
  • 本文由 发表于 2023年4月20日 08:12:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/76059668.html
匿名

发表评论

匿名网友

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

确定