Coding-哲学家进餐模型

并发编程经典问题之哲学家进餐。本文简单介绍了问题模型,并提供了一种 Python3 的解决方式。

问题模型
  • 问题描述
    • 一张圆桌上坐着5名哲学家,每两个哲学家之间的桌上摆一根筷子,桌子的中间是一碗米饭。
    • 哲学家们倾注毕生精力用于思考和进餐,哲学家在思考时,并不影响他人。只有当哲学家饥饿的时候,才试图拿起左、 右两根筷子(一根一根地拿起)。
    • 如果筷子已在他人手上,则需等待。
    • 饥饿的哲学家只有同时拿到了两根筷子才可以开始进餐,当进餐完毕后,放下筷子继续思考。

在这里插入图片描述

  • 问题分析
    • 5 个哲学家与左右邻居对其中间筷子的访问是互斥关系。
    • 5 个哲学家之间是异步关系。
    • 那么解决方法有两个:一个是让他们同时拿两个筷子;二是对每个哲学家的动作制定规则,避免饥饿或者死锁现象的发生。
    • 本题的关键是如何让一个哲学家拿到左右两个筷子而不造成死锁或者饥饿现象。
Talk is cheap, show me code
  • 解法一,一个筷子一把锁,所有筷子互斥,实现简单,但存在循环等待与死锁的可能。性能和饥饿情况如下所示:
# log 文件中是运行 100s 的结果
cat log | grep finish | awk '{print $1}'| sed 's/^Philosopher\[\([0-9]\)\].*$/\1/' | sort -n | uniq -c
     78 0	# 0 号哲学家进餐 78 次
     79 1	# 1 号哲学家进餐 79 次
     78 2	# 2 号哲学家进餐 78 次
     78 3	# 3 号哲学家进餐 78 次
     77 4	# 4 号哲学家进餐 77 次
  • 解法二,在解法一的基础上,把拿两个筷子的操作原子化,可以避免死锁问题。性能和饥饿情况如下所示:
# log 文件中是运行 100s 的结果
cat log | grep finish | awk '{print $1}'| sed 's/^Philosopher\[\([0-9]\)\].*$/\1/' | sort -n | uniq -c
     66 0  	# 0 号哲学家进餐 66 次
     68 1 	# 1 号哲学家进餐 68 次
     65 2	# 2 号哲学家进餐 65 次
     66 3	# 3 号哲学家进餐 66 次
     63 4	# 4 号哲学家进餐 63 次
  • 结论:解法一性能占优势,解法二 4 号哲学家出现轻微饥饿情况。解法二运行时间设置到 1000s 时结果如下,饥饿情况反而不明显了:
    676 0
    677 1
    675 2
    676 3
    675 4
  • 代码
from threading import Thread,Lock
from time import sleep

PHILO_NUM = 5

# 吃饭
def eat(num):
    print("Philosopher[" + str(num) + "] start to eat")
    sleep(1)
    print("Philosopher[" + str(num) + "] finish eating")

# 思考
def think(num):
    print("Philosopher[" + str(num) + "] start to think")
    sleep(1)
    print("Philosopher[" + str(num) + "] finish thinking")

# 解法一
def philosopher_1(num, locks):
    print("Philosopher[" + str(num) + "] Started")
    lock_left = locks[num]
    lock_right = locks[(num + 1) % PHILO_NUM]
    sleep(1)
    while True:
        lock_left.acquire()
        lock_right.acquire()
        eat(num)
        lock_right.release()
        lock_left.release()
        think(num)

# 解法二
def philosopher_2(num, c_locks, p_lock):
    print("Philosopher[" + str(num) + "] Started")
    lock_left = c_locks[num]
    lock_right = c_locks[(num + 1) % PHILO_NUM]
    sleep(1)
    while True:
        p_lock.acquire()
        lock_left.acquire()
        lock_right.acquire()
        p_lock.release()
        eat(num)
        lock_right.release()
        lock_left.release()
        think(num)

def main():
    
    # 筷子锁
    lock_chopsticks = []
    for i in range(PHILO_NUM):
        lock_chopsticks.append(Lock())
    
    # 步调锁
    lock_pick = Lock()
    
    # 启动哲学家线程
    thread_philo = []
    for i in range(PHILO_NUM):
        thread_philo.append(Thread(target=philosopher_1, args=(i, lock_chopsticks), daemon=True))
        thread_philo[-1].start()
        # thread_philo.append(Thread(target=philosopher_2, args=(i, lock_chopsticks, lock_pick), daemon=True))
        thread_philo[-1].start()
        
    input("\n\n==Enter Any Key To Exit==\n\n")
    exit()

if __name__ == '__main__':
    main() 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值