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

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

Deadlock in Housie Program. Producer-Consumer Pattern

问题

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

  1. package main
  2. import (
  3. "fmt"
  4. "math/rand"
  5. )
  6. type PersonID int
  7. func contains(s []int, e int) bool {
  8. for _, a := range s {
  9. if a == e {
  10. return true
  11. }
  12. }
  13. return false
  14. }
  15. func Person(called_number chan int, claim_prize chan PersonID, received chan bool, coupon []int, person_id PersonID) {
  16. numFound := 0
  17. for i := 0; i < len(coupon); i++ {
  18. current_number := <-called_number
  19. found := contains(coupon, current_number)
  20. if found {
  21. numFound++
  22. }
  23. if numFound == len(coupon) {
  24. claim_prize <- person_id
  25. } else {
  26. received <- true
  27. }
  28. }
  29. }
  30. func main() {
  31. var called_number chan int
  32. var claim_prize chan PersonID
  33. var received chan bool
  34. tokens := make([][]int, 3)
  35. for i := 0; i < 3; i++ {
  36. tokens[i] = make([]int, 12)
  37. for j := 0; j < 12; j++ {
  38. num := rand.Intn(100) + 1
  39. found := contains(tokens[i], num)
  40. for found {
  41. num = rand.Intn(100) + 1
  42. found = contains(tokens[i], num)
  43. }
  44. tokens[i][j] = num
  45. }
  46. }
  47. go Person(called_number, claim_prize, received, tokens[0], 0)
  48. go Person(called_number, claim_prize, received, tokens[1], 1)
  49. go Person(called_number, claim_prize, received, tokens[2], 2)
  50. claimants := make([]PersonID, 0)
  51. prev_called := make(map[int]bool)
  52. for i := 0; i < 100; i++ {
  53. if len(claimants) == 3 {
  54. break
  55. }
  56. num := rand.Intn(100) + 1
  57. _, ok := prev_called[num]
  58. for ok {
  59. num = rand.Intn(100) + 1
  60. _, ok = prev_called[num]
  61. }
  62. prev_called[num] = true
  63. called_number <- num
  64. for j := 0; j < 3; j++ {
  65. select {
  66. case _ = <-received:
  67. continue
  68. case pid := <-claim_prize:
  69. claimants = append(claimants, pid)
  70. }
  71. }
  72. }
  73. fmt.Println(claimants)
  74. }

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

  1. package main
  2. import (
  3. "fmt"
  4. "math/rand"
  5. )
  6. func contains(s []int, e int) bool {
  7. for _, a := range s {
  8. if a == e {
  9. return true
  10. }
  11. }
  12. return false
  13. }
  14. func Person(called_number chan int, claim_prize chan int, received chan bool, coupon []int, person_id int) {
  15. numFound := 0
  16. for current_number := range called_number {
  17. if contains(coupon, current_number) {
  18. numFound++
  19. }
  20. if numFound == len(coupon) {
  21. fmt.Println(person_id)
  22. claim_prize <- person_id
  23. } else {
  24. received <- true
  25. }
  26. }
  27. }
  28. func main() {
  29. var (
  30. called_number1 = make(chan int, 1)
  31. called_number2 = make(chan int, 1)
  32. called_number3 = make(chan int, 1)
  33. claim_prize = make(chan int, 1)
  34. received = make(chan bool, 1)
  35. )
  36. tokens := make([][]int, 3)
  37. for i := 0; i < 3; i++ {
  38. tokens[i] = make([]int, 12)
  39. for j := 0; j < 12; j++ {
  40. num := rand.Intn(100) + 1
  41. found := contains(tokens[i], num)
  42. for found {
  43. num = rand.Intn(100) + 1
  44. found = contains(tokens[i], num)
  45. }
  46. tokens[i][j] = num
  47. }
  48. }
  49. go Person(called_number1, claim_prize, received, tokens[0], 0)
  50. go Person(called_number2, claim_prize, received, tokens[1], 1)
  51. go Person(called_number3, claim_prize, received, tokens[2], 2)
  52. claimants := make([]int, 0)
  53. prev_called := make(map[int]bool)
  54. for i := 0; i < 100; i++ {
  55. if len(claimants) == 3 {
  56. break
  57. }
  58. num := rand.Intn(100) + 1
  59. _, ok := prev_called[num]
  60. for ok {
  61. num = rand.Intn(100) + 1
  62. _, ok = prev_called[num]
  63. }
  64. prev_called[num] = true
  65. if !contains(claimants, 0) {
  66. called_number1 <- num
  67. }
  68. if !contains(claimants, 1) {
  69. called_number2 <- num
  70. }
  71. if !contains(claimants, 2) {
  72. called_number3 <- num
  73. }
  74. for j := 0; j < 3; j++ {
  75. select {
  76. case _ = <-received:
  77. continue
  78. case pid := <-claim_prize:
  79. if pid == 0 { close(called_number1) }
  80. if pid == 1 { close(called_number2) }
  81. if pid == 2 { close(called_number3) }
  82. claimants = append(claimants, pid)
  83. }
  84. }
  85. }
  86. fmt.Println(claimants)
  87. }
  88. 编辑2这仍然发生死锁因为即使 goroutine 完成了我也没有减少等待的通道数量我进行了修改现在一切正常运行
  89. <details>
  90. <summary>英文:</summary>
  91. 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.
  92. package main
  93. import (
  94. &quot;fmt&quot;
  95. &quot;math/rand&quot;
  96. )
  97. type PersonID int
  98. func contains(s []int, e int) bool {
  99. for _, a := range s {
  100. if a == e {
  101. return true
  102. }
  103. }
  104. return false
  105. }
  106. func Person(called_number chan int, claim_prize chan PersonID, received chan bool, coupon []int, person_id PersonID) {
  107. numFound := 0
  108. for i := 0; i &lt; len(coupon); i++ {
  109. current_number := &lt;-called_number
  110. found := contains(coupon, current_number)
  111. if found {
  112. numFound++
  113. }
  114. if numFound == len(coupon) {
  115. claim_prize &lt;- person_id
  116. } else {
  117. received &lt;- true
  118. }
  119. }
  120. }
  121. func main() {
  122. var called_number chan int
  123. var claim_prize chan PersonID
  124. var received chan bool
  125. tokens := make([][]int, 3)
  126. for i := 0; i &lt; 3; i++ {
  127. tokens[i] = make([]int, 12)
  128. for j := 0; j &lt; 12; j++ {
  129. num := rand.Intn(100) + 1
  130. found := contains(tokens[i], num)
  131. for found {
  132. num = rand.Intn(100) + 1
  133. found = contains(tokens[i], num)
  134. }
  135. tokens[i][j] = num
  136. }
  137. }
  138. go Person(called_number, claim_prize, received, tokens[0], 0)
  139. go Person(called_number, claim_prize, received, tokens[1], 1)
  140. go Person(called_number, claim_prize, received, tokens[2], 2)
  141. claimants := make([]PersonID, 0)
  142. prev_called := make(map[int]bool)
  143. for i := 0; i &lt; 100; i++ {
  144. if len(claimants) == 3 {
  145. break
  146. }
  147. num := rand.Intn(100) + 1
  148. _, ok := prev_called[num]
  149. for ok {
  150. num = rand.Intn(100) + 1
  151. _, ok = prev_called[num]
  152. }
  153. prev_called[num] = true
  154. called_number &lt;- num
  155. for j := 0; j &lt; 3; j++ {
  156. select {
  157. case _ = &lt;-received:
  158. continue
  159. case pid := &lt;-claim_prize:
  160. claimants = append(claimants, pid)
  161. }
  162. }
  163. }
  164. fmt.Println(claimants)
  165. }
  166. EDIT:
  167. 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.
  168. package main
  169. import (
  170. &quot;fmt&quot;
  171. &quot;math/rand&quot;
  172. )
  173. func contains(s []int, e int) bool {
  174. for _, a := range s {
  175. if a == e {
  176. return true
  177. }
  178. }
  179. return false
  180. }
  181. func Person(called_number chan int, claim_prize chan int, received chan bool, coupon []int, person_id int) {
  182. numFound := 0
  183. for current_number := range called_number {
  184. if contains(coupon, current_number) {
  185. numFound++
  186. }
  187. if numFound == len(coupon) {
  188. fmt.Println(person_id)
  189. claim_prize &lt;- person_id
  190. } else {
  191. received &lt;- true
  192. }
  193. }
  194. }
  195. func main() {
  196. var (
  197. called_number1 = make(chan int, 1)
  198. called_number2 = make(chan int, 1)
  199. called_number3 = make(chan int, 1)
  200. claim_prize = make(chan int, 1)
  201. received = make(chan bool, 1)
  202. )
  203. tokens := make([][]int, 3)
  204. for i := 0; i &lt; 3; i++ {
  205. tokens[i] = make([]int, 12)
  206. for j := 0; j &lt; 12; j++ {
  207. num := rand.Intn(100) + 1
  208. found := contains(tokens[i], num)
  209. for found {
  210. num = rand.Intn(100) + 1
  211. found = contains(tokens[i], num)
  212. }
  213. tokens[i][j] = num
  214. }
  215. }
  216. go Person(called_number1, claim_prize, received, tokens[0], 0)
  217. go Person(called_number2, claim_prize, received, tokens[1], 1)
  218. go Person(called_number3, claim_prize, received, tokens[2], 2)
  219. claimants := make([]int, 0)
  220. prev_called := make(map[int]bool)
  221. for i := 0; i &lt; 100; i++ {
  222. if len(claimants) == 3 {
  223. break
  224. }
  225. num := rand.Intn(100) + 1
  226. _, ok := prev_called[num]
  227. for ok {
  228. num = rand.Intn(100) + 1
  229. _, ok = prev_called[num]
  230. }
  231. prev_called[num] = true
  232. if !contains(claimants, 0) {
  233. called_number1 &lt;- num
  234. }
  235. if !contains(claimants, 1) {
  236. called_number2 &lt;- num
  237. }
  238. if !contains(claimants, 2) {
  239. called_number3 &lt;- num
  240. }
  241. for j := 0; j &lt; 3; j++ {
  242. select {
  243. case _ = &lt;-received:
  244. continue
  245. case pid := &lt;-claim_prize:
  246. if pid == 0 { close(called_number1) }
  247. if pid == 1 { close(called_number2) }
  248. if pid == 2 { close(called_number3) }
  249. claimants = append(claimants, pid)
  250. }
  251. }
  252. }
  253. fmt.Println(claimants)
  254. }
  255. 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.
  256. </details>
  257. # 答案1
  258. **得分**: 2
  259. 几个问题
  260. 1. 你正在使用一个空通道所以它会永远阻塞但出于某种原因它没有引发错误
  261. 2. 另外你的第二个内部循环会无限期地阻塞因为它在等待读取但没有发送任何内容
  262. 3. `Person` 的循环结束后`called_number <- num` 会永远阻塞
  263. //编辑
  264. 部分可工作的代码http://play.golang.org/p/3At5nuJTuk
  265. 但是逻辑有问题你需要重新考虑一下
  266. <details>
  267. <summary>英文:</summary>
  268. Few problems:
  269. 1. You&#39;re using a nil channel, so it just blocks forever, for some reason it&#39;s not panicing.
  270. 2. Also, your second inner loop will block indefinitely because it&#39;s waiting to read but nothing is being sent.
  271. 3. After your `Person`&#39;s loop ends, `called_number &lt;- num` will block forever.
  272. //edit
  273. Kinda-working-code : http://play.golang.org/p/3At5nuJTuk
  274. But the logic is flawed, you will have to rethink it.
  275. </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:

确定