Golang实现的哲学家就餐问题变体

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

Golang implementation of dining philosophers variant problem

问题

我想实现一个变种的经典餐厅哲学家问题,其定义如下:

使用以下约束/修改实现餐厅哲学家问题。

  • 有5个哲学家共享筷子,每个相邻的哲学家之间有一根筷子。
  • 每个哲学家只能吃3次(不像我们在讲座中那样无限循环)。
  • 哲学家可以以任意顺序拿起筷子(不像我们在讲座中那样按最低编号优先)。
  • 为了吃饭,哲学家必须得到一个在自己的goroutine中执行的主持人的许可。
  • 主持人最多允许2个哲学家同时就餐。
  • 每个哲学家都有一个编号,从1到5。
  • 当一个哲学家开始吃饭(在获得必要的锁之后)时,他会单独打印一行“starting to eat”,其中是哲学家的编号。
  • 当一个哲学家吃完饭(在释放锁之前)时,他会单独打印一行“finishing eating”,其中是哲学家的编号。

我实现了以下代码:

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. )
  6. // 定义变量
  7. var numPhilo int = 5
  8. var numCS int = 5
  9. var eatTimes int = 3
  10. var numEatingPhilo int = 2
  11. type Host struct {
  12. // 允许哲学家就餐的通道
  13. requestChannel chan *Philo
  14. // 守护进程终止信号的通道
  15. quitChannel chan int
  16. // 当前就餐的哲学家的记录
  17. eatingPhilos map[int]bool
  18. // 修改eatingPhilos变量的互斥锁
  19. mu sync.Mutex
  20. }
  21. // 管理允许就餐的哲学家的守护进程函数
  22. func (pHost *Host) manage() {
  23. // 守护进程在后台运行,只有在接收到终止信号时才退出
  24. for {
  25. // 如果通道已满,则释放队列头部的哲学家
  26. if len(pHost.requestChannel) == numEatingPhilo {
  27. finished := <-pHost.requestChannel
  28. currEating := make([]int, 0, numPhilo)
  29. for tmpIdx, eating := range pHost.eatingPhilos {
  30. if eating {
  31. currEating = append(currEating, tmpIdx)
  32. }
  33. }
  34. fmt.Printf("%v 正在就餐,清理 %d\n", currEating, finished.idx)
  35. pHost.eatingPhilos[finished.idx] = false
  36. }
  37. select {
  38. case <-pHost.quitChannel:
  39. fmt.Println("停止主持")
  40. return
  41. default:
  42. }
  43. }
  44. }
  45. type ChopS struct {
  46. mu sync.Mutex
  47. }
  48. type Philo struct {
  49. // 哲学家的索引
  50. idx int
  51. // 哲学家就餐的次数
  52. numEat int
  53. leftCS, rightCS *ChopS
  54. host *Host
  55. }
  56. func (pPhilo *Philo) eat(wg *sync.WaitGroup) {
  57. for pPhilo.numEat < eatTimes {
  58. // 当哲学家打算就餐时,锁定相应的筷子
  59. pPhilo.leftCS.mu.Lock()
  60. pPhilo.rightCS.mu.Lock()
  61. // 在通道中预留一个位置用于就餐
  62. // 如果通道缓冲区已满,将被阻塞,直到通道空间被释放
  63. pPhilo.host.requestChannel <- pPhilo
  64. pPhilo.host.mu.Lock()
  65. pPhilo.host.eatingPhilos[pPhilo.idx] = true
  66. pPhilo.host.mu.Unlock()
  67. pPhilo.numEat++
  68. fmt.Printf("开始就餐 %d,第 %d 次\n", pPhilo.idx, pPhilo.numEat)
  69. fmt.Printf("结束就餐 %d,第 %d 次\n", pPhilo.idx, pPhilo.numEat)
  70. pPhilo.rightCS.mu.Unlock()
  71. pPhilo.leftCS.mu.Unlock()
  72. wg.Done()
  73. }
  74. }
  75. func main() {
  76. var wg sync.WaitGroup
  77. requestChannel := make(chan *Philo, numEatingPhilo)
  78. quitChannel := make(chan int, 1)
  79. host := Host{requestChannel: requestChannel, quitChannel: quitChannel, eatingPhilos: make(map[int]bool)}
  80. CSticks := make([]*ChopS, numCS)
  81. for i := 0; i < numCS; i++ {
  82. CSticks[i] = new(ChopS)
  83. }
  84. philos := make([]*Philo, numPhilo)
  85. for i := 0; i < numPhilo; i++ {
  86. philos[i] = &Philo{idx: i + 1, numEat: 0, leftCS: CSticks[i], rightCS: CSticks[(i+1)%5], host: &host}
  87. }
  88. go host.manage()
  89. wg.Add(numPhilo * eatTimes)
  90. for i := 0; i < numPhilo; i++ {
  91. go philos[i].eat(&wg)
  92. }
  93. wg.Wait()
  94. host.quitChannel <- 1
  95. }

然而,我注意到程序在某些情况下实际上会失败,例如:

  1. 开始就餐 5,第 1
  2. 结束就餐 5,第 1
  3. 开始就餐 2,第 1
  4. 结束就餐 2,第 1
  5. [5 2] 正在就餐,清理 5
  6. [2] 正在就餐,清理 2
  7. [] 正在就餐,清理 5
  8. 开始就餐 5,第 2
  9. 结束就餐 5,第 2
  10. 开始就餐 5,第 3
  11. 结束就餐 5,第 3
  12. [5 2] 正在就餐,清理 2
  13. [5] 正在就餐,清理 5
  14. 开始就餐 2,第 2
  15. 结束就餐 2,第 2
  16. [4] 正在就餐,清理 4
  17. 开始就餐 4,第 1
  18. 结束就餐 4,第 1
  19. [] 正在就餐,清理 2
  20. 开始就餐 4,第 2
  21. 结束就餐 4,第 2
  22. [4] 正在就餐,清理 4
  23. 开始就餐 4,第 3
  24. 结束就餐 4,第 3
  25. 开始就餐 2,第 3
  26. 结束就餐 2,第 3
  27. 开始就餐 1,第 1
  28. 结束就餐 1,第 1
  29. [2 4 1] 正在就餐,清理 4
  30. [2 1] 正在就餐,清理 1
  31. [2] 正在就餐,清理 3
  32. 开始就餐 1,第 2
  33. 结束就餐 1,第 2
  34. [1 2] 正在就餐,清理 1
  35. 开始就餐 3,第 1
  36. 结束就餐 3,第 1
  37. 开始就餐 3,第 2
  38. 结束就餐 3,第 2
  39. [3 2] 正在就餐,清理 1
  40. [2 3] 正在就餐,清理 3
  41. 开始就餐 3,第 3
  42. 结束就餐 3,第 3
  43. 开始就餐 1,第 3
  44. 结束就餐 1,第 3
  45. 停止主持

有时候似乎哲学家无法同时就餐,并且根据日志,记录哲学家的映射表行为异常。

请问有人能指出实现的哪个部分不正确吗?有什么明显的错误或不良实践吗?

英文:

I would like to implement a variant of the classical dining philosophers problem which has the definition as:

Implement the dining philosopher’s problem with the following constraints/modifications.

  • There should be 5 philosophers sharing chopsticks, with one chopstick between each adjacent pair of philosophers.
  • Each philosopher should eat only 3 times (not in an infinite loop as we did in lecture)
  • The philosophers pick up the chopsticks in any order, not lowest-numbered first (which we did in lecture).
  • In order to eat, a philosopher must get permission from a host which executes in its own goroutine.
  • The host allows no more than 2 philosophers to eat concurrently.
  • Each philosopher is numbered, 1 through 5.
  • When a philosopher starts eating (after it has obtained necessary locks) it prints “starting to eat ” on a line by itself, where is the number of the philosopher.
  • When a philosopher finishes eating (before it has released its locks) it prints “finishing eating ” on a line by itself, where is the number of the philosopher.

I implemented the following code:

  1. package main
  2. import (
  3. &quot;fmt&quot;
  4. &quot;sync&quot;
  5. )
  6. // define variables
  7. var numPhilo int = 5
  8. var numCS int = 5
  9. var eatTimes int = 3
  10. var numEatingPhilo int = 2
  11. type Host struct {
  12. // channel for allowed philosopher for eating
  13. requestChannel chan *Philo
  14. // channel for terminate signal for the daemon
  15. quitChannel chan int
  16. // bookkeeping of the current eating philosophers
  17. eatingPhilos map[int]bool
  18. // mutex to lock the modification of the eatingPhilos variable
  19. mu sync.Mutex
  20. }
  21. // daemon function to manage the allowed philosophers
  22. func (pHost *Host) manage() {
  23. // daemon serving in the backend and only exits for terminate signal
  24. for {
  25. // if the channel is full, release the head of the queue
  26. if len(pHost.requestChannel) == numEatingPhilo {
  27. finished := &lt;-pHost.requestChannel
  28. currEating := make([]int, 0, numPhilo)
  29. for tmpIdx, eating := range pHost.eatingPhilos {
  30. if eating {
  31. currEating = append(currEating, tmpIdx)
  32. }
  33. }
  34. fmt.Printf(&quot;%v have been eating, clearing up %d\n&quot;, currEating, finished.idx)
  35. pHost.eatingPhilos[finished.idx] = false
  36. }
  37. select {
  38. case &lt;-pHost.quitChannel:
  39. fmt.Println(&quot;stop hosting&quot;)
  40. return
  41. default:
  42. }
  43. }
  44. }
  45. type ChopS struct {
  46. mu sync.Mutex
  47. }
  48. type Philo struct {
  49. // index of the philosopher
  50. idx int
  51. // number of times the philosopher has eaten
  52. numEat int
  53. leftCS, rightCS *ChopS
  54. host *Host
  55. }
  56. func (pPhilo *Philo) eat(wg *sync.WaitGroup) {
  57. for pPhilo.numEat &lt; eatTimes {
  58. // once the philosopher intends to eat, lock the corresponding chopsticks
  59. pPhilo.leftCS.mu.Lock()
  60. pPhilo.rightCS.mu.Lock()
  61. // reserve a slot in the channel for eating
  62. // if channel buffer is full, this is blocked until channel space is released
  63. pPhilo.host.requestChannel &lt;- pPhilo
  64. pPhilo.host.mu.Lock()
  65. pPhilo.host.eatingPhilos[pPhilo.idx] = true
  66. pPhilo.host.mu.Unlock()
  67. pPhilo.numEat++
  68. fmt.Printf(&quot;starting to eat %d for %d time\n&quot;, pPhilo.idx, pPhilo.numEat)
  69. fmt.Printf(&quot;finishing eating %d for %d time\n&quot;, pPhilo.idx, pPhilo.numEat)
  70. pPhilo.rightCS.mu.Unlock()
  71. pPhilo.leftCS.mu.Unlock()
  72. wg.Done()
  73. }
  74. }
  75. func main() {
  76. var wg sync.WaitGroup
  77. requestChannel := make(chan *Philo, numEatingPhilo)
  78. quitChannel := make(chan int, 1)
  79. host := Host{requestChannel: requestChannel, quitChannel: quitChannel, eatingPhilos: make(map[int]bool)}
  80. CSticks := make([]*ChopS, numCS)
  81. for i := 0; i &lt; numCS; i++ {
  82. CSticks[i] = new(ChopS)
  83. }
  84. philos := make([]*Philo, numPhilo)
  85. for i := 0; i &lt; numPhilo; i++ {
  86. philos[i] = &amp;Philo{idx: i + 1, numEat: 0, leftCS: CSticks[i], rightCS: CSticks[(i+1)%5], host: &amp;host}
  87. }
  88. go host.manage()
  89. wg.Add(numPhilo * eatTimes)
  90. for i := 0; i &lt; numPhilo; i++ {
  91. go philos[i].eat(&amp;wg)
  92. }
  93. wg.Wait()
  94. host.quitChannel &lt;- 1
  95. }

However, I noticed that the program is actually failing in some cases, i.e.

  1. starting to eat 5 for 1 time
  2. finishing eating 5 for 1 time
  3. starting to eat 2 for 1 time
  4. finishing eating 2 for 1 time
  5. [5 2] have been eating, clearing up 5
  6. [2] have been eating, clearing up 2
  7. [] have been eating, clearing up 5
  8. starting to eat 5 for 2 time
  9. finishing eating 5 for 2 time
  10. starting to eat 5 for 3 time
  11. finishing eating 5 for 3 time
  12. [5 2] have been eating, clearing up 2
  13. [5] have been eating, clearing up 5
  14. starting to eat 2 for 2 time
  15. finishing eating 2 for 2 time
  16. [4] have been eating, clearing up 4
  17. starting to eat 4 for 1 time
  18. finishing eating 4 for 1 time
  19. [] have been eating, clearing up 2
  20. starting to eat 4 for 2 time
  21. finishing eating 4 for 2 time
  22. [4] have been eating, clearing up 4
  23. starting to eat 4 for 3 time
  24. finishing eating 4 for 3 time
  25. starting to eat 2 for 3 time
  26. finishing eating 2 for 3 time
  27. starting to eat 1 for 1 time
  28. finishing eating 1 for 1 time
  29. [2 4 1] have been eating, clearing up 4
  30. [2 1] have been eating, clearing up 1
  31. [2] have been eating, clearing up 3
  32. starting to eat 1 for 2 time
  33. finishing eating 1 for 2 time
  34. [1 2] have been eating, clearing up 1
  35. starting to eat 3 for 1 time
  36. finishing eating 3 for 1 time
  37. starting to eat 3 for 2 time
  38. finishing eating 3 for 2 time
  39. [3 2] have been eating, clearing up 1
  40. [2 3] have been eating, clearing up 3
  41. starting to eat 3 for 3 time
  42. finishing eating 3 for 3 time
  43. starting to eat 1 for 3 time
  44. finishing eating 1 for 3 time
  45. stop hosting

where sometimes it seems the philosophers cannot eat concurrently, and also according to the logs, the bookkeeping map is acting weird.

Could someone please point out which part of the implementation is improper? Anything obviously wrong or bad practice?

答案1

得分: 1

你在这里发布的代码存在数据竞争问题(Host.eatingPhilos在没有任何保护的情况下从多个Go协程中访问)。在在code review上发布问题之前,你解决了这些问题,这在某种程度上解决了问题(但引入了其他问题)。

我在code review上对此进行了详细回答,并提供了一些建议等。由于你在那里发布的代码与这里的代码不同,所以重复回答没有太大意义。然而,我将包含我的建议解决方案,因为它采用了与你采用的方法显著不同的方法(为了解决一些逻辑问题)。请注意,这个解决方案采用了一个简单的方法来解决“饥饿哲学家”问题(每个哲学家只有一根筷子,所以没有人可以拿到第二根)。参见Wikipedia页面了解其他更好的解决方案(但我想你想使用Go协程)。

警告 - 下面的代码可能包含错误!

Playground

  1. package main
  2. import (
  3. "fmt"
  4. "math/rand"
  5. "sync"
  6. "time"
  7. "golang.org/x/exp/slices"
  8. )
  9. const (
  10. numPhilo = 5
  11. eatTimes = 3
  12. numEatingPhilo = 2
  13. )
  14. type eatRequest struct {
  15. who int // 请求者的编号
  16. finishedFnChan chan func() // 当批准请求时,将在此通道上发送一个带有完成时调用的函数
  17. }
  18. // simulateHost - 主持人必须在哲学家吃饭之前提供许可
  19. // 当通道关闭时退出
  20. func simulateHost(requestChannel <-chan eatRequest) {
  21. awaitRequest := requestChannel
  22. finishedChan := make(chan struct {
  23. who int
  24. done chan struct{}
  25. })
  26. var whoEating []int // 跟踪当前正在吃饭的人
  27. for {
  28. select {
  29. case request, ok := <-awaitRequest:
  30. if !ok {
  31. return // 通道关闭意味着我们完成了(finishedChan保证为空)
  32. }
  33. // 检查一下 - 确认哲学家不贪心!(永远不应该发生)
  34. if slices.Index(whoEating, request.who) != -1 {
  35. panic("同一个哲学家发出多个请求")
  36. }
  37. whoEating = append(whoEating, request.who) // 新请求总是放在最后
  38. fmt.Printf("%d 开始吃饭(当前正在吃饭的人:%v)\n", request.who, whoEating)
  39. // 告诉哲学家,并提供他们在完成后告诉我们的方法
  40. request.finishedFnChan <- func() {
  41. d := make(chan struct{})
  42. finishedChan <- struct {
  43. who int
  44. done chan struct{}
  45. }{who: request.who, done: d}
  46. <-d // 等待请求被处理(确保我们永远不会有两个来自同一个哲学家的活动请求)
  47. }
  48. case fin := <-finishedChan:
  49. idx := slices.Index(whoEating, fin.who)
  50. if idx == -1 {
  51. panic("哲学家多次停止吃饭!")
  52. }
  53. whoEating = append(whoEating[:idx], whoEating[idx+1:]...) // 删除元素
  54. fmt.Printf("%d 完成吃饭(当前正在吃饭的人:%v)\n", fin.who, whoEating)
  55. close(fin.done)
  56. }
  57. // 哲学家吃饭的人数发生了变化
  58. if len(whoEating) < numEatingPhilo {
  59. awaitRequest = requestChannel
  60. } else {
  61. awaitRequest = nil // 忽略新的吃饭请求,直到有哲学家吃完(nil通道永远不会被选中)
  62. }
  63. }
  64. }
  65. // ChopS 表示一根筷子
  66. type ChopS struct {
  67. mu sync.Mutex
  68. idx int // 包括索引可以使调试更简单
  69. }
  70. // philosopher 模拟一个哲学家(大脑在一个容器中!)
  71. func philosopher(philNum int, leftCS, rightCS *ChopS, requestToEat chan<- eatRequest) {
  72. for numEat := 0; numEat < eatTimes; numEat++ {
  73. // 一旦哲学家打算吃饭,锁定相应的筷子
  74. for {
  75. leftCS.mu.Lock()
  76. // 尝试获取右边的筷子 - 如果有人拿着,我们替换左边的筷子并重试
  77. // (以避免死锁)
  78. if rightCS.mu.TryLock() {
  79. break
  80. }
  81. leftCS.mu.Unlock()
  82. }
  83. // 我们有筷子,但需要主持人的许可
  84. ffc := make(chan func()) // 当被接受时,我们将收到一个完成吃饭时调用的函数
  85. requestToEat <- eatRequest{
  86. who: philNum,
  87. finishedFnChan: ffc,
  88. }
  89. doneEating := <-ffc
  90. fmt.Printf("哲学家 %d 开始吃饭(第 %d 次)\n", philNum, numEat)
  91. time.Sleep(time.Millisecond * time.Duration(rand.Intn(200))) // 吃饭需要随机的时间
  92. fmt.Printf("哲学家 %d 完成吃饭(第 %d 次)\n", philNum, numEat)
  93. rightCS.mu.Unlock()
  94. leftCS.mu.Unlock()
  95. doneEating() // 告诉主持人我们吃完了
  96. }
  97. fmt.Printf("哲学家 %d 已经吃饱了\n", philNum)
  98. }
  99. func main() {
  100. CSticks := make([]*ChopS, numPhilo)
  101. for i := 0; i < numPhilo; i++ {
  102. CSticks[i] = &ChopS{idx: i}
  103. }
  104. requestChannel := make(chan eatRequest)
  105. var wg sync.WaitGroup
  106. wg.Add(numPhilo)
  107. for i := 0; i < numPhilo; i++ {
  108. go func(philNo int) {
  109. philosopher(philNo, CSticks[philNo-1], CSticks[philNo%numPhilo], requestChannel)
  110. wg.Done()
  111. }(i + 1)
  112. }
  113. go func() {
  114. wg.Wait()
  115. close(requestChannel)
  116. }()
  117. simulateHost(requestChannel)
  118. }
英文:

The code you posted here contains data races (Host.eatingPhilos is accessed from multiple go-routines without any protection). You resolved these before posting your question on code review which went some way towards a working solution (but introduced other issues).

I answered this fully on code review and provided some feedback etc. As the code you posted there differs from what is here there is little point in duplicating the answer. However, I will include my suggested solution because it adopts a significantly different approach to that you took (in order to work around a few logic issues). Note that this solution takes an easy way out of the "starving philosopher" issue (each philosopher has one chopstick, so none can get a second). See the Wikipedia page for some other, better, solutions (but I figure you wanted to use go routines).

warning - the below may well contain bugs!

Playground

  1. package main
  2. import (
  3. &quot;fmt&quot;
  4. &quot;math/rand&quot;
  5. &quot;sync&quot;
  6. &quot;time&quot;
  7. &quot;golang.org/x/exp/slices&quot;
  8. )
  9. const (
  10. numPhilo = 5
  11. eatTimes = 3
  12. numEatingPhilo = 2
  13. )
  14. type eatRequest struct {
  15. who int // Who is making the request
  16. finishedFnChan chan func() // When approves a response will be sent on this channel with a function to call when done
  17. }
  18. // simulateHost - the host must provide permission before a philosopher can eat
  19. // Exits when channel closed
  20. func simulateHost(requestChannel &lt;-chan eatRequest) {
  21. awaitRequest := requestChannel
  22. finishedChan := make(chan struct {
  23. who int
  24. done chan struct{}
  25. })
  26. var whoEating []int // tracks who is currently eating
  27. for {
  28. select {
  29. case request, ok := &lt;-awaitRequest:
  30. if !ok {
  31. return // Closed channel means that we are done (finishedChan is guaranteed to be empty)
  32. }
  33. // Sanity check - confirm that philosopher is not being greedy! (should never happen)
  34. if slices.Index(whoEating, request.who) != -1 {
  35. panic(&quot;Multiple requests from same philosopher&quot;)
  36. }
  37. whoEating = append(whoEating, request.who) // New request always goes at the end
  38. fmt.Printf(&quot;%d started eating (currently eating %v)\n&quot;, request.who, whoEating)
  39. // Let philosopher know and provide means for them to tell us when done
  40. request.finishedFnChan &lt;- func() {
  41. d := make(chan struct{})
  42. finishedChan &lt;- struct {
  43. who int
  44. done chan struct{}
  45. }{who: request.who, done: d}
  46. &lt;-d // Wait until request has been processed (ensure we should never have two active requests from one philosopher)
  47. }
  48. case fin := &lt;-finishedChan:
  49. idx := slices.Index(whoEating, fin.who)
  50. if idx == -1 {
  51. panic(&quot;philosopher stopped eating multiple times!&quot;)
  52. }
  53. whoEating = append(whoEating[:idx], whoEating[idx+1:]...) // delete the element
  54. fmt.Printf(&quot;%d completed eating (currently eating %v)\n&quot;, fin.who, whoEating)
  55. close(fin.done)
  56. }
  57. // There has been a change in the number of philosopher&#39;s eating
  58. if len(whoEating) &lt; numEatingPhilo {
  59. awaitRequest = requestChannel
  60. } else {
  61. awaitRequest = nil // Ignore new eat requests until a philosopher finishes (nil channel will never be selected)
  62. }
  63. }
  64. }
  65. // ChopS represents a single chopstick
  66. type ChopS struct {
  67. mu sync.Mutex
  68. idx int // Including the index can make debugging simpler
  69. }
  70. // philosopher simulates a Philosopher (brain in a vat!)
  71. func philosopher(philNum int, leftCS, rightCS *ChopS, requestToEat chan&lt;- eatRequest) {
  72. for numEat := 0; numEat &lt; eatTimes; numEat++ {
  73. // once the philosopher intends to eat, lock the corresponding chopsticks
  74. for {
  75. leftCS.mu.Lock()
  76. // Attempt to get the right Chopstick - if someone else has it we replace the left chopstick and try
  77. // again (in order to avoid deadlocks)
  78. if rightCS.mu.TryLock() {
  79. break
  80. }
  81. leftCS.mu.Unlock()
  82. }
  83. // We have the chopsticks but need the hosts permission
  84. ffc := make(chan func()) // when accepted we will receive a function to call when done eating
  85. requestToEat &lt;- eatRequest{
  86. who: philNum,
  87. finishedFnChan: ffc,
  88. }
  89. doneEating := &lt;-ffc
  90. fmt.Printf(&quot;philosopher %d starting to eat (%d feed)\n&quot;, philNum, numEat)
  91. time.Sleep(time.Millisecond * time.Duration(rand.Intn(200))) // Eating takes a random amount of time
  92. fmt.Printf(&quot;philosopher %d finished eating (%d feed)\n&quot;, philNum, numEat)
  93. rightCS.mu.Unlock()
  94. leftCS.mu.Unlock()
  95. doneEating() // Tell host that we have finished eating
  96. }
  97. fmt.Printf(&quot;philosopher %d is full\n&quot;, philNum)
  98. }
  99. func main() {
  100. CSticks := make([]*ChopS, numPhilo)
  101. for i := 0; i &lt; numPhilo; i++ {
  102. CSticks[i] = &amp;ChopS{idx: i}
  103. }
  104. requestChannel := make(chan eatRequest)
  105. var wg sync.WaitGroup
  106. wg.Add(numPhilo)
  107. for i := 0; i &lt; numPhilo; i++ {
  108. go func(philNo int) {
  109. philosopher(philNo, CSticks[philNo-1], CSticks[philNo%numPhilo], requestChannel)
  110. wg.Done()
  111. }(i + 1)
  112. }
  113. go func() {
  114. wg.Wait()
  115. close(requestChannel)
  116. }()
  117. simulateHost(requestChannel)
  118. }

huangapple
  • 本文由 发表于 2022年8月4日 05:33:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/73228045.html
匿名

发表评论

匿名网友

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

确定