如何通过3个goroutine无死锁地交替打印狗、猫和鱼,每个动物打印3次?

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

Go:How to print dog, cat and fish alternately for 3 times through 3 goroutine without deadlock?

问题

  1. 如何使用sync.WaitGroup解决死锁问题?

我的代码可以正确打印结果,但会导致死锁。

我目前能想到的解决方法是:用time.Sleep替换sync.WaitGroup。

但我想知道如何使用sync.WaitGroup解决死锁问题?

  1. 这是运行的结果:
dog: 0
cat: 0
fish: 0
dog: 1
cat: 1
fish: 1
dog: 2
cat: 2
fish: 2
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [semacquire]:
sync.runtime_Semacquire(0xeaeeb0)
        D:/GoSDK/go1.17.3/src/runtime/sema.go:56 +0x25
sync.(*WaitGroup).Wait(0x60)
        D:/GoSDK/go1.17.3/src/sync/waitgroup.go:130 +0x71
  1. 这是我的代码:
func main(){
	wg := sync.WaitGroup{}
	wg.Add(3)
	const Count = 3
	ch1, ch2, ch3 := make(chan bool), make(chan bool), make(chan bool)

	go func() {
		defer wg.Done()
		for i := 0; i < Count; i++ {
			<-ch1
			fmt.Println("dog:", i)
			//notice 2
			ch2 <- true
		}
	}()
	go func() {
		defer wg.Done()
		for i := 0; i < Count; i++ {
			//wait 1
			<-ch2
			fmt.Println("cat:", i)
			//notice 3
			ch3 <- true
		}
	}()
	go func() {
		defer wg.Done()
		for i := 0; i < Count; i++ {
			//wait 2
			<-ch3
			fmt.Println("fish:", i)
			//notice 1
			ch1 <- true
		}
	}()
	//notic 1
	ch1 <- true
	wg.Wait()
}
英文:

1.How to use sync.WaitGroup to solve deadlock?

My code can print the result correctly, but it will cause deadlock.

What I can think of now is:replace sync.WaitGroup with time.Sleep.

But I want to know how to use sync.WaitGroup to solve deadlock?

  1. This is the result of the run:
dog: 0
cat: 0                                                           
fish: 0                                                          
dog: 1                                                           
cat: 1                                                           
fish: 1                                                          
dog: 2                                                           
cat: 2                                                           
fish: 2                                                          
fatal error: all goroutines are asleep - deadlock!               
                                                                 
goroutine 1 [semacquire]:                                        
sync.runtime_Semacquire(0xeaeeb0)                                
        D:/GoSDK/go1.17.3/src/runtime/sema.go:56 +0x25           
sync.(*WaitGroup).Wait(0x60)                                     
        D:/GoSDK/go1.17.3/src/sync/waitgroup.go:130 +0x71    
  1. This is my code:
func main(){
	wg := sync.WaitGroup{}
	wg.Add(3)
	const Count = 3
	ch1, ch2, ch3 := make(chan bool), make(chan bool), make(chan bool)

	go func() {
		defer wg.Done()
		for i := 0; i &lt; Count; i++ {
			&lt;-ch1
			fmt.Println(&quot;dog:&quot;, i)
			//notice 2
			ch2 &lt;- true
		}
	}()
	go func() {
		defer wg.Done()
		for i := 0; i &lt; Count; i++ {
			//wait 1
			&lt;-ch2
			fmt.Println(&quot;cat:&quot;, i)
			//notice 3
			ch3 &lt;- true
		}
	}()
	go func() {
		defer wg.Done()
		for i := 0; i &lt; Count; i++ {
			//wait 2
			&lt;-ch3
			fmt.Println(&quot;fish:&quot;, i)
			//notice 1
			ch1 &lt;- true
		}
	}()
	//notic 1
	ch1 &lt;- true
	wg.Wait()
}

答案1

得分: 0

问题在于没有任何goroutine在等待ch1。所以在第三个goroutine的最后一次迭代中,锁定了(ch1 &lt;- true)。
所以你需要一种方法来识别链条停止的情况。
我快速而谦虚的建议:

func main() {
    wg := sync.WaitGroup{}
    wg.Add(3)
    const Count = 3
    ch1, ch2, ch3 := make(chan bool), make(chan bool), make(chan bool)
    chainEnded := make(chan struct{})

    go func() {
        defer wg.Done()
        for i := 0; i < Count; i++ {
            <-ch1
            fmt.Println("dog:", i)
            //notice 2
            ch2 <- true
        }
        chainEnded <- struct{}{}
    }()
    go func() {
        defer wg.Done()
        for i := 0; i < Count; i++ {
            //wait 1
            <-ch2
            fmt.Println("cat:", i)
            //notice 3
            ch3 <- true
        }
    }()
    go func() {
        defer wg.Done()
        for i := 0; i < Count; i++ {
            //wait 2
            <-ch3
            fmt.Println("fish:", i)
            //notice 1
            ch1 <- true
        }
    }()
    //notic 1
    ch1 <- true
    <-chainEnded // wait for the loop to end and send to this chan
    <-ch1        // unblock the attempt to chain of the last routine
    wg.Wait()
}
英文:

The issue here is that no go routine is waiting for ch1. So on the last iteration of the third goroutine locks (ch1 &lt;- true).
So what you need is a way to recognize that the chain stopped.
My fast and humble suggestion:

func main() {
	wg := sync.WaitGroup{}
	wg.Add(3)
	const Count = 3
	ch1, ch2, ch3 := make(chan bool), make(chan bool), make(chan bool)
	chainEnded := make(chan struct{})

	go func() {
		defer wg.Done()
		for i := 0; i &lt; Count; i++ {
			&lt;-ch1
			fmt.Println(&quot;dog:&quot;, i)
			//notice 2
			ch2 &lt;- true
		}
		chainEnded &lt;- struct{}{}
	}()
	go func() {
		defer wg.Done()
		for i := 0; i &lt; Count; i++ {
			//wait 1
			&lt;-ch2
			fmt.Println(&quot;cat:&quot;, i)
			//notice 3
			ch3 &lt;- true
		}
	}()
	go func() {
		defer wg.Done()
		for i := 0; i &lt; Count; i++ {
			//wait 2
			&lt;-ch3
			fmt.Println(&quot;fish:&quot;, i)
			//notice 1
			ch1 &lt;- true
		}
	}()
	//notic 1
	ch1 &lt;- true
	&lt;-chainEnded // wait for the loop to end and send to this chan
	&lt;-ch1        // unblock the attempt to chain of the last routine
	wg.Wait()
}

huangapple
  • 本文由 发表于 2022年5月1日 19:15:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/72076194.html
匿名

发表评论

匿名网友

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

确定