我正在尝试实施一个housie游戏,其中goroutine产生数字,另外3个goroutines检查它们是否在他们的标记中并通知制作者是否所有数字都已生成。我已经通过以下方式在golang中实现了它。这导致死锁。知道为什么会这样吗?这是一个“作业问题”,我只是在实现它去学习更好。
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完成后我也没有减少等待的频道数量。这一切都行得通。
答案 0 :(得分:2)
几个问题:
你正在使用一个nil频道,所以它只是永远阻止,因为某些原因它没有慌张。
此外,您的第二个内部循环将无限期阻止,因为它等待阅读但没有发送任何内容。
在Person
循环结束后,called_number <- num
将永久阻止。
//编辑 有点工作代码:http://play.golang.org/p/3At5nuJTuk
但逻辑是有缺陷的,你必须重新考虑它。