0 00:00:00,000 --> 00:00:07,120 1 00:00:07,160 --> 00:00:08,960 好 那我们来看一下 2 00:00:09,000 --> 00:00:11,440 哲学家就餐问题一个实现 3 00:00:11,480 --> 00:00:13,360 在lab7中我们给大家提供了 4 00:00:13,400 --> 00:00:14,720 基于信号量的 5 00:00:14,760 --> 00:00:16,280 哲学家就餐问题的实现 6 00:00:16,320 --> 00:00:18,760 在这里面 我们需要大家去实现 7 00:00:18,800 --> 00:00:21,320 基于管程和条件变量的 8 00:00:21,360 --> 00:00:22,760 哲学家就餐问题 9 00:00:22,800 --> 00:00:23,800 既然是基于管程 10 00:00:23,840 --> 00:00:24,920 我们就需要考虑 11 00:00:24,960 --> 00:00:26,480 怎么对管程进行定义 12 00:00:26,520 --> 00:00:27,640 和完成相应的初始化 13 00:00:27,680 --> 00:00:29,360 这是第一步工作 14 00:00:29,400 --> 00:00:31,000 可以看到 15 00:00:31,040 --> 00:00:33,040 对于哲学家就餐问题呢 16 00:00:33,080 --> 00:00:36,320 我们和前面的讲解是一样的 17 00:00:36,360 --> 00:00:38,800 他会有一个哲学家一个状态 18 00:00:38,840 --> 00:00:42,680 包括了思考 饥饿 吃饭 19 00:00:42,720 --> 00:00:43,960 这三个状态 20 00:00:44,000 --> 00:00:45,360 同时我们还定义了 21 00:00:45,400 --> 00:00:48,320 跟这五个哲学家相关的条件变量 22 00:00:48,360 --> 00:00:51,120 这个self一共有5个 23 00:00:51,160 --> 00:00:52,760 那他的初始化代码在哪呢 24 00:00:52,800 --> 00:00:53,680 一开始的时候 25 00:00:53,720 --> 00:00:54,640 我们会把哲学家 26 00:00:54,680 --> 00:00:56,960 都初始化为thinking状态 27 00:00:57,000 --> 00:00:58,880 这就是他的初始化的工作 28 00:00:58,920 --> 00:01:01,320 这就是管程初始化一个 29 00:01:01,360 --> 00:01:04,680 大致一个表述 30 00:01:04,720 --> 00:01:06,800 我们会产生 31 00:01:06,840 --> 00:01:09,560 五个哲学家的内核线程 32 00:01:09,600 --> 00:01:11,760 来模拟哲学家的行为 33 00:01:11,800 --> 00:01:13,000 那这个内核线程呢 34 00:01:13,040 --> 00:01:14,200 他干事情是什么呢 35 00:01:14,240 --> 00:01:15,760 他实际上是一个循环 36 00:01:15,800 --> 00:01:17,160 在这个循环里面 37 00:01:17,200 --> 00:01:19,520 完成thinking然后感觉饥饿 38 00:01:19,560 --> 00:01:23,040 然后就尝试着去取两个筷子 39 00:01:23,080 --> 00:01:24,760 左手和右手的筷子 40 00:01:24,800 --> 00:01:25,960 当然这个取筷子呢 41 00:01:26,000 --> 00:01:27,320 实际上是调的是我们说 42 00:01:27,360 --> 00:01:29,200 管程里面的一些操作 43 00:01:29,240 --> 00:01:31,400 这个操作需要完成一定的 44 00:01:31,440 --> 00:01:33,520 同步互斥的一些要求 45 00:01:33,560 --> 00:01:35,880 然后拿到筷子之后他会eating 46 00:01:35,920 --> 00:01:37,120 如果拿不到呢 47 00:01:37,160 --> 00:01:38,760 很自然 他就会去睡眠 48 00:01:38,800 --> 00:01:40,600 好eating完之后呢 49 00:01:40,640 --> 00:01:42,040 他会把筷子再放回去 50 00:01:42,080 --> 00:01:43,680 这就是他大致一个过程 51 00:01:43,720 --> 00:01:45,320 需要注意的是 在这里面 52 00:01:45,360 --> 00:01:47,000 取筷子和放筷子呢 53 00:01:47,040 --> 00:01:49,080 都是管程的操作 54 00:01:49,120 --> 00:01:50,360 那我们看看 管程里面 55 00:01:50,400 --> 00:01:55,520 怎么来实现这些操作 56 00:01:55,560 --> 00:01:56,440 在管程里面 57 00:01:56,480 --> 00:01:58,360 我们提供了三个函数 58 00:01:58,400 --> 00:02:01,040 一个是取筷子 一个是放筷子 59 00:02:01,080 --> 00:02:03,080 还有一个是辅助取筷子 60 00:02:03,120 --> 00:02:04,760 和放筷子的一个Test函数 61 00:02:04,800 --> 00:02:06,560 那么这个函数实现 62 00:02:06,600 --> 00:02:08,760 和我们用信号量方式实现呢 63 00:02:08,800 --> 00:02:10,640 有很多类似地方 但是有不同 64 00:02:10,680 --> 00:02:12,080 需要注意 在哪呢 65 00:02:12,120 --> 00:02:14,440 关键两个地方 一个Wait_cv 66 00:02:14,480 --> 00:02:16,200 一个是Signal_cv 67 00:02:16,240 --> 00:02:17,680 这就是调用的信号量的 68 00:02:17,720 --> 00:02:20,120 等待或唤醒换作 69 00:02:20,160 --> 00:02:22,200 我们简单看一下 70 00:02:22,240 --> 00:02:25,080 第一个是pickup 就是拿筷子 71 00:02:25,120 --> 00:02:27,000 那他首先会把他自身的状态 72 00:02:27,040 --> 00:02:28,960 置成hungry 表示我现在饥饿了 73 00:02:29,000 --> 00:02:30,120 然后执行test 74 00:02:30,160 --> 00:02:33,080 看我自身的左侧邻居和右侧邻居 75 00:02:33,120 --> 00:02:35,400 是否是不处于eating状态 76 00:02:35,440 --> 00:02:36,640 如果不处于eating状态 77 00:02:36,680 --> 00:02:37,840 也就意味着他可以拿到筷子 78 00:02:37,880 --> 00:02:39,680 所以他会执行一个Signal_cv 79 00:02:39,720 --> 00:02:42,240 来把自身设置成一个 80 00:02:42,280 --> 00:02:44,360 可以去吃的一个状态 81 00:02:44,400 --> 00:02:48,040 同时 还会继续去进一步执行 82 00:02:48,080 --> 00:02:50,120 但是如果说他不这样的话 83 00:02:50,160 --> 00:02:52,040 那么他会执行什么呢 84 00:02:52,080 --> 00:02:53,480 执行完test返回之后 85 00:02:53,520 --> 00:02:54,880 如果他不是一种吃的状态 86 00:02:54,920 --> 00:02:57,000 那意味着他现在无法 87 00:02:57,040 --> 00:02:58,720 拿到足够的筷子 88 00:02:58,760 --> 00:03:01,480 他会通过执行Wait_cv操作呢 89 00:03:01,520 --> 00:03:04,560 来完成一个等待的一个过程 90 00:03:04,600 --> 00:03:06,120 这是拿筷子的过程 91 00:03:06,160 --> 00:03:07,720 可以看出 在拿筷子里面 92 00:03:07,760 --> 00:03:09,520 有可能他拿到筷子 93 00:03:09,560 --> 00:03:10,760 处于一个吃的状态 94 00:03:10,800 --> 00:03:12,000 也有可能拿不到筷子 95 00:03:12,040 --> 00:03:13,800 然后调用Wait_cv呢 96 00:03:13,840 --> 00:03:16,480 使自身进入一个等待状态 97 00:03:16,520 --> 00:03:17,800 当吃完饭之后呢 98 00:03:17,840 --> 00:03:20,360 他会再执行一个putdown操作 99 00:03:20,400 --> 00:03:21,080 那putdown呢 100 00:03:21,120 --> 00:03:22,360 会把他的左手筷子 101 00:03:22,400 --> 00:03:23,720 和右手筷子放回去 102 00:03:23,760 --> 00:03:25,760 在放的过程中 还会去探测 103 00:03:25,800 --> 00:03:28,280 他的左侧邻居 或者是右侧邻居 104 00:03:28,320 --> 00:03:30,920 是否可以处于eating状态 105 00:03:30,960 --> 00:03:32,720 如果发现他们可以处于eating状态 106 00:03:32,760 --> 00:03:35,720 他就会通过执行一个Signal_cv 107 00:03:35,760 --> 00:03:37,720 来把相应的邻居给唤醒 108 00:03:37,760 --> 00:03:39,880 且把他们状态置成eating 109 00:03:39,920 --> 00:03:41,840 这就是pickup和putdown的 110 00:03:41,880 --> 00:03:43,400 大致的一个实现 111 00:03:43,440 --> 00:03:47,720 那么大家基于大致的流程呢 112 00:03:47,760 --> 00:03:50,320 可以去尝试在lab7中完成 113 00:03:50,360 --> 00:03:53,400 基于管程和条件变量的一个 114 00:03:53,440 --> 00:03:55,000 哲学家就餐问题的实现 115 00:03:55,040 --> 00:03:56,160 当然这取决于 116 00:03:56,200 --> 00:03:58,040 你能够正确地完成管程 117 00:03:58,080 --> 00:03:59,720 和条件变量一个实现 118 00:03:59,760 --> 00:04:00,840 而我们条件变量 119 00:04:00,880 --> 00:04:05,840 是基于信号量来完成的 120 00:04:05,880 --> 00:04:07,160 好 那通过前面的讲解呢 121 00:04:07,200 --> 00:04:10,480 大家能够去了解信号量 条件变量 122 00:04:10,520 --> 00:04:12,400 管程是如何实现的 123 00:04:12,440 --> 00:04:14,440 以及他们之间的相互关系 124 00:04:14,480 --> 00:04:16,680 还有就是基于这些机制呢 125 00:04:16,720 --> 00:04:17,960 我们怎么能够去完成 126 00:04:18,000 --> 00:04:19,200 相应的同步互斥的问题 127 00:04:19,240 --> 00:04:21,320 比如说哲学家就餐问题 128 00:04:21,400 --> 00:04:23,160 好 祝大家实验顺利 129 00:04:23,200 --> 00:04:23,240