我正在參加圍棋課程,該課程的作業如下:
通過以下約束/修改來實作哲學家就餐的問題。
應該有 5 個哲學家共用筷子,每對相鄰的哲學家之間有一根筷子。
每個哲學家應該只吃 3 次(而不是像我們在講座中那樣無限回圈)。
哲學家以任何順序拿起筷子,而不是從最小的數字開始(我們在講座中就是這樣做的)。
為了吃飯,哲學家必須獲得在其自己的 goroutine 中執行的主機的許可。
主人允許不超過 2 個哲學家同時進餐。
每個哲學家都有編號,從 1 到 5。
當哲學家開始進食時(在獲得必要的鎖后),它會在一行上單獨列印“開始進食”,其中是哲學家的編號。
當哲學家吃完(在它釋放鎖之前)它會在一行上列印“吃完”,其中是哲學家的號碼。
我的實作:
package main
import (
"fmt"
"io"
"math/rand"
"os"
"sync"
"time"
)
const (
NumPhilosophers = 5
NumEatMaxTimes = 3
NumMaxAllowedToEat = 2
)
type chopstick struct{ sync.Mutex }
type philosopher struct {
num int
cs []*chopstick
}
func setTable() []*philosopher {
cs := make([]*chopstick, NumPhilosophers)
for i := 0; i < NumPhilosophers; i {
cs[i] = new(chopstick)
}
ph := make([]*philosopher, NumPhilosophers)
for i := 0; i < NumPhilosophers; i {
ph[i] = &philosopher{i 1, []*chopstick{cs[i], cs[(i 1)%NumPhilosophers]}}
}
return ph
}
func (ph philosopher) eat(sem chan int, wg *sync.WaitGroup, w io.Writer) {
for i := 0; i < NumEatMaxTimes; i {
/* Ask host for permission to eat */
sem <- 1
/*
Pick any of the left or right chopsticks.
Notice how the methods on the Mutex can be called directly on a chopstick due to embedding.
*/
firstCS := rand.Intn(2)
secondCS := (firstCS 1) % 2
ph.cs[firstCS].Lock()
ph.cs[secondCS].Lock()
fmt.Fprintf(w, "Starting to eat %d\n", ph.num)
x := rand.Intn(NumEatMaxTimes)
time.Sleep(time.Duration(x) * time.Second)
fmt.Fprintf(w, "Finishing eating %d\n", ph.num)
ph.cs[secondCS].Unlock()
ph.cs[firstCS].Unlock()
<-sem
}
wg.Done()
}
func main() {
run(os.Stdout)
}
func run(w io.Writer) {
var sem = make(chan int, NumMaxAllowedToEat)
rand.Seed(time.Now().UnixNano())
var wg sync.WaitGroup
allPh := setTable()
wg.Add(len(allPh))
for _, ph := range allPh {
go ph.eat(sem, &wg, w)
}
wg.Wait()
}
單元測驗:
func TestRun(t *testing.T) {
var out bytes.Buffer
run(&out)
lines := strings.Split(strings.ReplaceAll(out.String(), "\r\n", "\n"), "\n")
eating := make(map[int]bool)
timesEaten := make(map[int]int)
for _, line := range lines {
if line == "" {
continue
}
fmt.Println(line)
tokens := strings.Fields(line)
i, err := strconv.Atoi(tokens[len(tokens)-1])
if err != nil {
t.Errorf("Bad line: %s", line)
}
s := strings.ToLower(tokens[0])
if s == "starting" {
if len(eating) > (NumMaxAllowedToEat - 1) {
t.Errorf("%v are eating at the same time", eating)
}
_, ok := eating[i]
if ok {
t.Errorf("%d started before finishing", i)
}
eating[i] = true
} else if s == "finishing" {
_, ok := eating[i]
if !ok {
t.Errorf("%d finished without starting", i)
}
delete(eating, i)
timesEaten[i] = timesEaten[i] 1
}
}
for k, v := range timesEaten {
if v > NumEatMaxTimes {
t.Errorf("%d ate %d times", k, v)
}
}
if len(timesEaten) != NumPhilosophers {
t.Error("One or more didn't get to eat")
}
}
問題是,測驗隨機失敗。下面是一個執行(添加了行號):
1. Starting to eat 5
2. Starting to eat 2
3. Finishing eating 2
4. Finishing eating 5
5. Starting to eat 3
6. Starting to eat 1
7. Finishing eating 1
8. Finishing eating 3
9. Starting to eat 2
10. Starting to eat 4
11. Finishing eating 4
12. Starting to eat 5
13. Finishing eating 2
14. Finishing eating 5
15. Starting to eat 3
16. Finishing eating 3
17. Starting to eat 1
18. Finishing eating 4
19. Finishing eating 1
20. Starting to eat 5
21. Finishing eating 5
22. Starting to eat 3
23. Finishing eating 3
24. Starting to eat 4
25. Starting to eat 2
26. Finishing eating 2
27. Starting to eat 1
28. Finishing eating 4
29. Finishing eating 1
--- FAIL: TestRun (12.01s)
main_test.go:43: 4 finished without starting
Philosopher 4 在第 10 和 24 行開始并在第 11、18 和 28 行結束。第 28 行是不匹配的,所以測驗正確地抱怨。但是,我很難找到錯誤。你能幫我嗎?
uj5u.com熱心網友回復:
回答我自己的問題,結果是byes.Buffer 不是執行緒安全的。我最終使用go-fakeio庫進行測驗,如下所示。
s, err := fakeio.Stderr().Stdout().Do(run)
if err != nil {
t.Errorf("%v", err)
}
其余的測驗保持不變。main.run函式不再需要 an,io.Writer因為fakeio庫替換了 stderr 和 stdout。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/383615.html
下一篇:如何在代碼中設定斷點來除錯PHP
