哲学家就餐问题python_哲学家就餐问题与死锁

问题描述

哲学家就餐问题(Dining philosophers problem)是在计算机科学中的一个经典问题,用来演示在并发计算中多线程同步(Synchronization)时产生的问题。 哲学家就餐问题可以这样表述:五位哲学家围绕一个圆桌就坐,桌上摆着五 支 筷子。哲学家的状态可能是“思考”或者“饥饿”。如果饥饿,哲学家将拿起他两边的筷子并进餐一段时间。进餐结束,哲学家就会放回筷子。哲学家从来不交谈,这就很危险,会产生每个哲学家都拿着左边的筷子,一直在等右边的筷子的情况。

可以用如下的代码表示这个问题:

import threading

import time

import random

import os

import sys

import signal

class Watcher():

def __init__(self):

self.child = os.fork()

if self.child == 0:

return

else:

self.watch()

def watch(self):

try:

os.wait()

except KeyboardInterrupt:

self.kill()

sys.exit()

def kill(self):

try:

os.kill(self.child, signal.SIGKILL)

except OSError:

pass

class Philosopher(threading.Thread):

def __init__(self, left, right):

threading.Thread.__init__(self)

self.left = left

self.right = right

def run(self):

while True:

# now thinking random time

time.sleep(random.random())

# now picking up chopsticks

with self.left:

with self.right:

# now eating

print '%s is eating %s' % (threading.currentThread().getName(), time.ctime())

time.sleep(random.random())

if __name__ == '__main__':

num = 5

chopsticks = [threading.Lock() for _ in range(num)]

philosophers = [Philosopher(chopsticks[(_ - 1) % num],

chopsticks[_ % num])for _ in range(num)]

Watcher()

for p in philosophers:

p.start()

上面这段代码产生死锁的概率比较小,运行了两个小时还没出现(可见死锁问题调试时难以被发现)。为了让死锁更容易出现我们稍加修改,假设哲学家们拿起左面的筷子后突然忘记了自己要吃饭,沉思一会后,再拿起右面的筷子去吃饭。

with self.left:

time.sleep(random.random())

with self.right:

Output

Thread-4 is eating Tue Sep 13 13:15:20 2016

Thread-3 is eating Tue Sep 13 13:15:21 2016

...

Thread-3 is eating Tue Sep 13 13:16:15 2016

Thread-2 is eating Tue Sep 13 13:16:15 2016

# block here

当一个线程想要使用多把锁时,就需要考虑死锁。 有一种方案是在获取第二把锁的时候设置超时时间,这种方案虽然避免了无尽的死锁,但这并不是一个足够好的方案。首先,这个方案并不能避免死锁——它只是提供了一种从死锁中恢复的手段。其次,这个方案会受到活锁现象的影响——如果所有死锁线程同时超时,它们极有可能再次陷入死锁。虽然死锁没有永远持续下去,但对资源的争夺状况却没有得到任何改善。

下面给出两种避免死锁的方法

固定顺序获取

有一个简单的方法可以避开死锁——总是按照一个全局的固定的顺序获取多把锁。

class Philosopher(threading.Thread):

def __init__(self, left, right):

threading.Thread.__init__(self)

if id(left) < id(right):

self.first = left

self.second = right

else:

self.first = right

self.second = left

def run(self):

while True:

# now thinking random time

time.sleep(random.random())

# now picking up chopsticks

with self.first:

time.sleep(random.random())

with self.second:

# now eating

print '%s is eating %s' % (threading.currentThread().getName(), time.ctime())

time.sleep(random.random())

条件变量

class Philosopher(threading.Thread):

def __init__(self, con):

threading.Thread.__init__(self)

self.is_eating = False

self.con = con

def set_around(self, left, right):

self.left = left

self.right = right

def thinking(self):

with self.con:

self.is_eating = False

self.left.con.notify()

self.right.con.notify()

time.sleep(random.random())

def eating(self):

with self.con:

while self.left.is_eating or self.right.is_eating:

self.con.wait()

self.is_eating = True

print '%s is eating %s' % (threading.currentThread().getName(), time.ctime())

time.sleep(random.random())

def run(self):

while True:

self.thinking()

self.eating()

if __name__ == '__main__':

num = 5

con = threading.Condition()

philosophers = [Philosopher(con) for _ in range(num)]

for i in range(num):

philosophers[i].set_around(

philosophers[(i - 1) % num], philosophers[i % num])

Watcher()

for p in philosophers:

p.start()

与之前不同,我们将竞争从对筷子的竞争转换成了对状态的判断:仅当哲学家的左右都没有进餐时,他才可以进餐。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值