我正在用Go编写Philosophers Dining解决方案。我的解决方案很简单:检查两个叉子是否都可用。如果是这样,将它们都捡起来。如果没有,则两者都保留。
但是,我遇到了一个奇怪的并发错误,即即使在显式设置为false之后,fork的可用性仍然为true。我的Fork
声明如下:
type Fork struct {
mu sync.Mutex
avail bool
}
func (f *Fork) PickUp() bool {
f.mu.Lock()
if f.avail == false {
f.mu.Unlock()
return false
}
f.avail = false
fmt.Println("set false")
f.mu.Unlock()
return true
}
func (f *Fork) PutDown() {
f.mu.Lock()
f.avail = true
f.mu.Unlock()
}
当Philosopher调用
PickUp()
时,程序将等待互斥锁。如果此时分叉可用,则Fork
将其可用性布尔值设置为false,然后返回true表示操作成功。哲学家是这样写的:
type Philosopher struct {
seatNum int
}
func (phl *Philosopher) StartDining(forkList [9]Fork) {
for {
fmt.Println(forkList[phl.seatNum], phl.seatNum)
if forkList[phl.seatNum].PickUp() {
fmt.Println("Philo ", phl.seatNum, " picked up fork ", phl.seatNum)
if forkList[phl.getLeftSpace()].PickUp() {
fmt.Println("Philo ", phl.seatNum, " picked up fork ", phl.getLeftSpace())
fmt.Println("Philo ", phl.seatNum, " has both forks; eating...")
time.Sleep(5 * time.Second)
forkList[phl.seatNum].PutDown()
forkList[phl.getLeftSpace()].PutDown()
fmt.Println("Philo ", phl.seatNum, " put down forks.")
} else {
forkList[phl.seatNum].PutDown()
}
}
}
}
(注意:
getLeftSpace()
函数已被排除,因为它的实现是无关紧要的;它只是获取左侧空间的索引。)Philosopher
的实现非常简单:它检查是否可以得到第一个fork。然后,检查是否可以得到第二个分叉。如果不能,则放下第一个叉子。如果可以,则将两者同时按住5秒钟,然后将两者放下。为了测试目的,我只限于两个哲学家。但是,这不能正常工作。哲学家0拿起第一个叉子,然后拿起第二个叉子。我已验证这些分叉的可用性已设置为false。至此,哲学家1被互斥锁锁定。但是,一旦Philo 0释放了互斥量,Philo 1便会进入该功能。
此时的预期结果是
PickUp()
函数返回false;由于货叉不再可用,因此无法将其捡起。但是,该功能不会执行此操作;它返回true,并允许Philo 1拿起叉子!更神秘的是,到Philo 1拿起叉子时,叉子的可用性为
true
,即使Philo 0明确将它们设置为false!这是我的调试输出:{{0 0} true} 0 # Fork 0 is available
set false # Fork 0 has been picked up
Philo 0 picked up fork 0 # Repsonse from Philo 0 confirming the above
{{0 0} true} 0 # Fork 1 is available
set false # Fork 1 has been picked up
Philo 0 picked up fork 1 # Response from Philo 0 confirming the above
Philo 0 has both forks; eating... # As Philo 0 has both forks, they can now eat...
{{0 0} true} 1 **# Philo 1 checks Fork 0's availability, **which is true?**
set false # Philo 1 sets Fork 0's availability to false
Philo 1 picked up fork 1 # Response of ^
{{0 0} true} 1
set false
Philo 1 picked up fork 2
Philo 1 has both forks; eating...
Philo 1永远不应该能够拿起叉子。由于Philo 1被互斥锁锁定,并且
PickUp
的唯一两个退出条件是在可用性为false之后,因此该分叉可能不可用。是的为什么是这样?我该如何解决这个问题?
最佳答案
我认为问题几乎可以肯定是您的StartDining
方法的签名:
func (phl *Philosopher) StartDining(forkList [9]Fork)
Go中的数组是按值传递的,因此,每次调用
StartDining
时,都将传递分叉的副本。哲学家在完全分开的桌子旁就餐!尝试将指针传递给数组。