Housie程序中的死锁。生产者-消费者模式

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

Deadlock in Housie Program. Producer-Consumer Pattern

问题

我正在尝试实现一个 housie 游戏,其中一个 goroutine 生成数字,另外三个 goroutine 检查这些数字是否在它们的令牌中,并在所有数字都被生成后通知生成者。我已经用以下方式在 Golang 中实现了它。这导致了死锁。有什么想法为什么会发生这种情况吗?这是一个“作业问题”,我只是为了更好地学习 Go 而实现它。

package main

import (
	"fmt"
	"math/rand"
)

type PersonID int

func contains(s []int, e int) bool {
	for _, a := range s {
		if a == e {
			return true
		}
	}
	return false
}

func Person(called_number chan int, claim_prize chan PersonID, received chan bool, coupon []int, person_id PersonID) {
	numFound := 0
	for i := 0; i < len(coupon); i++ {
		current_number := <-called_number
		found := contains(coupon, current_number)
		if found {
			numFound++
		}
		if numFound == len(coupon) {
			claim_prize <- person_id
		} else {
			received <- true
		}
	}
}

func main() {
	var called_number chan int
	var claim_prize chan PersonID
	var received chan bool

	tokens := make([][]int, 3)
	for i := 0; i < 3; i++ {
		tokens[i] = make([]int, 12)
		for j := 0; j < 12; j++ {
			num := rand.Intn(100) + 1
			found := contains(tokens[i], num)
			for found {
				num = rand.Intn(100) + 1
				found = contains(tokens[i], num)
			}
			tokens[i][j] = num
		}
	}

	go Person(called_number, claim_prize, received, tokens[0], 0)
	go Person(called_number, claim_prize, received, tokens[1], 1)
	go Person(called_number, claim_prize, received, tokens[2], 2)

	claimants := make([]PersonID, 0)
	prev_called := make(map[int]bool)
	for i := 0; i < 100; i++ {
		if len(claimants) == 3 {
			break
		}
		num := rand.Intn(100) + 1
		_, ok := prev_called[num]
		for ok {
			num = rand.Intn(100) + 1
			_, ok = prev_called[num]
		}
		prev_called[num] = true
		called_number <- num
		for j := 0; j < 3; j++ {
			select {
			case _ = <-received:
				continue
			case pid := <-claim_prize:
				claimants = append(claimants, pid)
			}
		}
	}

	fmt.Println(claimants)
}

编辑:
确切的问题是生产者需要将数字发送给每个消费者。当消费者接收到其令牌中的所有数字时,它可以声明获奖。根据 @OneOfOne 的说法,我对程序进行了一些更改。更改的内容是现在为每个消费者都有一个单独的通道,并在它声明获奖后关闭它。以下是新程序,它仍然发生死锁。

package main

import (
	"fmt"
	"math/rand"
)

func contains(s []int, e int) bool {
	for _, a := range s {
		if a == e {
			return true
		}
	}
	return false
}

func Person(called_number chan int, claim_prize chan int, received chan bool, coupon []int, person_id int) {
	numFound := 0
	for current_number := range called_number {
		if contains(coupon, current_number) {
			numFound++
		}
		if numFound == len(coupon) {
			fmt.Println(person_id)
			claim_prize <- person_id
		} else {
			received <- true
		}
	}
}

func main() {
	var (
		called_number1 = make(chan int, 1)
		called_number2 = make(chan int, 1)
		called_number3 = make(chan int, 1)
		claim_prize    = make(chan int, 1)
		received       = make(chan bool, 1)
	)

	tokens := make([][]int, 3)
	for i := 0; i < 3; i++ {
		tokens[i] = make([]int, 12)
		for j := 0; j < 12; j++ {
			num := rand.Intn(100) + 1
			found := contains(tokens[i], num)
			for found {
				num = rand.Intn(100) + 1
				found = contains(tokens[i], num)
			}
			tokens[i][j] = num
		}
	}

	go Person(called_number1, claim_prize, received, tokens[0], 0)
	go Person(called_number2, claim_prize, received, tokens[1], 1)
	go Person(called_number3, claim_prize, received, tokens[2], 2)

	claimants := make([]int, 0)
	prev_called := make(map[int]bool)
	for i := 0; i < 100; i++ {
		if len(claimants) == 3 {
			break
		}
		num := rand.Intn(100) + 1
		_, ok := prev_called[num]
		for ok {
			num = rand.Intn(100) + 1
			_, ok = prev_called[num]
		}
		prev_called[num] = true
		if !contains(claimants, 0) {
			called_number1 <- num
		}
		if !contains(claimants, 1) {
			called_number2 <- num
		}
		if !contains(claimants, 2) {
			called_number3 <- num
		}
		for j := 0; j < 3; j++ {
			select {
			case _ = <-received:
				continue
			case pid := <-claim_prize:
				if pid == 0 { close(called_number1) }
				if pid == 1 { close(called_number2) }
				if pid == 2 { close(called_number3) }
				claimants = append(claimants, pid)
			}
		}
	}
	fmt.Println(claimants)
}

编辑2这仍然发生死锁因为即使 goroutine 完成了我也没有减少等待的通道数量我进行了修改现在一切正常运行

<details>
<summary>英文:</summary>

I am trying to implement a housie game where a goroutine produces numbers, 3 other goroutines check if these are in their tokens and inform the producer if all their numbers were produced. I have implemented it in golang in the following way. This results in a deadlock. Any idea why this is happening? This is a &quot;homework problem&quot;, I am just implementing it in go to learn go better. 

    package main
    
    import (
    	&quot;fmt&quot;
    	&quot;math/rand&quot;
    )
    
    type PersonID int
    
    func contains(s []int, e int) bool {
    	for _, a := range s {
    		if a == e {
    			return true
    		}
    	}
    	return false
    }
    
    func Person(called_number chan int, claim_prize chan PersonID, received chan bool, coupon []int, person_id PersonID) {
    	numFound := 0
    	for i := 0; i &lt; len(coupon); i++ {
    		current_number := &lt;-called_number
    		found := contains(coupon, current_number)
    		if found {
    			numFound++
    		}
    		if numFound == len(coupon) {
    			claim_prize &lt;- person_id
    		} else {
    			received &lt;- true
    		}
    	}
    }
    
    func main() {
    	var called_number chan int
    	var claim_prize chan PersonID
    	var received chan bool
    
    	tokens := make([][]int, 3)
    	for i := 0; i &lt; 3; i++ {
    		tokens[i] = make([]int, 12)
    		for j := 0; j &lt; 12; j++ {
    			num := rand.Intn(100) + 1
    			found := contains(tokens[i], num)
    			for found {
    				num = rand.Intn(100) + 1
    				found = contains(tokens[i], num)
    			}
    			tokens[i][j] = num
    		}
    	}
    
    	go Person(called_number, claim_prize, received, tokens[0], 0)
    	go Person(called_number, claim_prize, received, tokens[1], 1)
    	go Person(called_number, claim_prize, received, tokens[2], 2)
    
    	claimants := make([]PersonID, 0)
    	prev_called := make(map[int]bool)
    	for i := 0; i &lt; 100; i++ {
    		if len(claimants) == 3 {
    			break
    		}
    		num := rand.Intn(100) + 1
    		_, ok := prev_called[num]
    		for ok {
    			num = rand.Intn(100) + 1
    			_, ok = prev_called[num]
    		}
    		prev_called[num] = true
    		called_number &lt;- num
    		for j := 0; j &lt; 3; j++ {
    			select {
    			case _ = &lt;-received:
    				continue
    			case pid := &lt;-claim_prize:
    				claimants = append(claimants, pid)
    			}
    		}
    	}
    
    	fmt.Println(claimants)
    }

EDIT:
The exact problem is that that the producer needs to send the number to each of the consumers. When a consumer receives all the numbers in it&#39;s token, it can claim the prize. Based on what @OneOfOne said, I have made some changes to the program. The changes are that now there is a separate channels for each of the consumers and I am closing it after it claims a prize. Below is the new program, it still deadlocks. 

    package main
    
    import (
    	&quot;fmt&quot;
    	&quot;math/rand&quot;
    )
    
    func contains(s []int, e int) bool {
    	for _, a := range s {
    		if a == e {
    			return true
    		}
    	}
    	return false
    }
    
    func Person(called_number chan int, claim_prize chan int, received chan bool, coupon []int, person_id int) {
    	numFound := 0
    	for current_number := range called_number {
    		if contains(coupon, current_number) {
    			numFound++
    		}
    		if numFound == len(coupon) {
    			fmt.Println(person_id)
    			claim_prize &lt;- person_id
    		} else {
    			received &lt;- true
    		}
    	}
    }
    
    func main() {
    	var (
    		called_number1 = make(chan int, 1)
    		called_number2 = make(chan int, 1)
    		called_number3 = make(chan int, 1)
    		claim_prize    = make(chan int, 1)
    		received       = make(chan bool, 1)
    	)
    
    	tokens := make([][]int, 3)
    	for i := 0; i &lt; 3; i++ {
    		tokens[i] = make([]int, 12)
    		for j := 0; j &lt; 12; j++ {
    			num := rand.Intn(100) + 1
    			found := contains(tokens[i], num)
    			for found {
    				num = rand.Intn(100) + 1
    				found = contains(tokens[i], num)
    			}
    			tokens[i][j] = num
    		}
    	}
    
    	go Person(called_number1, claim_prize, received, tokens[0], 0)
    	go Person(called_number2, claim_prize, received, tokens[1], 1)
    	go Person(called_number3, claim_prize, received, tokens[2], 2)
    
    	claimants := make([]int, 0)
    	prev_called := make(map[int]bool)
    	for i := 0; i &lt; 100; i++ {
    		if len(claimants) == 3 {
    			break
    		}
    		num := rand.Intn(100) + 1
    		_, ok := prev_called[num]
    		for ok {
    			num = rand.Intn(100) + 1
    			_, ok = prev_called[num]
    		}
    		prev_called[num] = true
    		if !contains(claimants, 0) {
    			called_number1 &lt;- num
    		}
    		if !contains(claimants, 1) {
    			called_number2 &lt;- num
    		}
    		if !contains(claimants, 2) {
    			called_number3 &lt;- num
    		}
    		for j := 0; j &lt; 3; j++ {
    			select {
    			case _ = &lt;-received:
    				continue
    			case pid := &lt;-claim_prize:
    				if pid == 0 { close(called_number1) }
    				if pid == 1 { close(called_number2) }
    				if pid == 2 { close(called_number3) }
    				claimants = append(claimants, pid)
    			}
    		}
    	}
    	fmt.Println(claimants)
    }

EDIT2: This still deadlocked because I was not reducing the number of channels to wait for even after the goroutines were completed. Did that and everything works. 

</details>


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

几个问题

1. 你正在使用一个空通道所以它会永远阻塞但出于某种原因它没有引发错误

2. 另外你的第二个内部循环会无限期地阻塞因为它在等待读取但没有发送任何内容

3.  `Person` 的循环结束后`called_number <- num` 会永远阻塞

//编辑
部分可工作的代码http://play.golang.org/p/3At5nuJTuk

但是逻辑有问题你需要重新考虑一下

<details>
<summary>英文:</summary>

Few problems:

1. You&#39;re using a nil channel, so it just blocks forever, for some reason it&#39;s not panicing.

2. Also, your second inner loop will block indefinitely because it&#39;s waiting to read but nothing is being sent.

3. After your `Person`&#39;s loop ends, `called_number &lt;- num` will block forever.


//edit
Kinda-working-code : http://play.golang.org/p/3At5nuJTuk

But the logic is flawed, you will have to rethink it.

</details>



huangapple
  • 本文由 发表于 2015年4月11日 09:52:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/29573148.html
匿名

发表评论

匿名网友

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

确定