Housie 程序中的死锁。生产者消费者模式
Deadlock in Housie Program. Producer-Consumer Pattern
我正在尝试实现一个 housie 游戏,其中一个 goroutine 生成数字,其他 3 个 goroutine 检查这些是否在它们的令牌中,并通知生产者它们的所有数字是否都已生成。我已经通过以下方式在 golang 中实现了它。这会导致死锁。知道为什么会这样吗?这是一个"homework problem",我只是在go中实现它以更好地学习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)
}
EDIT2:这仍然陷入僵局,因为即使在 goroutine 完成后我也没有减少要等待的通道数。这样做了,一切正常。
几个问题:
你正在使用一个 nil 通道,所以它会永远阻塞,出于某种原因它不会恐慌。
此外,您的第二个内部循环将无限期阻塞,因为它正在等待读取但未发送任何内容。
在你的Person
循环结束后,called_number <- num
将永远阻塞。
//编辑
有点工作代码:http://play.golang.org/p/3At5nuJTuk
但是逻辑有问题,你得重新考虑一下。
我正在尝试实现一个 housie 游戏,其中一个 goroutine 生成数字,其他 3 个 goroutine 检查这些是否在它们的令牌中,并通知生产者它们的所有数字是否都已生成。我已经通过以下方式在 golang 中实现了它。这会导致死锁。知道为什么会这样吗?这是一个"homework problem",我只是在go中实现它以更好地学习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)
}
EDIT2:这仍然陷入僵局,因为即使在 goroutine 完成后我也没有减少要等待的通道数。这样做了,一切正常。
几个问题:
你正在使用一个 nil 通道,所以它会永远阻塞,出于某种原因它不会恐慌。
此外,您的第二个内部循环将无限期阻塞,因为它正在等待读取但未发送任何内容。
在你的
Person
循环结束后,called_number <- num
将永远阻塞。
//编辑 有点工作代码:http://play.golang.org/p/3At5nuJTuk
但是逻辑有问题,你得重新考虑一下。