0 00:00:00,000 --> 00:00:06,760 1 00:00:06,840 --> 00:00:08,160 下面我们来介绍 2 00:00:08,200 --> 00:00:09,720 如何利用信号量 3 00:00:09,760 --> 00:00:12,080 来解决同步互斥问题 4 00:00:12,120 --> 00:00:13,240 这就是我们这里说的 5 00:00:13,280 --> 00:00:14,960 信号量的使用 6 00:00:15,000 --> 00:00:17,160 那首先呢我们把信号量 7 00:00:17,200 --> 00:00:19,320 可以分成两类 8 00:00:19,360 --> 00:00:21,280 一类叫二进制信号量 9 00:00:21,320 --> 00:00:25,320 它的初值呢是0或者是1 10 00:00:25,360 --> 00:00:27,600 而另一类呢叫资源信号量 11 00:00:27,640 --> 00:00:29,640 它的资源数目呢 12 00:00:29,680 --> 00:00:32,760 可以是任意的非负整数 13 00:00:32,800 --> 00:00:35,520 这两类信号量呢它们俩是等价的 14 00:00:35,560 --> 00:00:37,560 也就说我用其中的一个 15 00:00:37,600 --> 00:00:39,680 可以构造出另一个 16 00:00:39,720 --> 00:00:41,680 可以由第一个构造出第二个 17 00:00:41,720 --> 00:00:44,400 也可以由第二个构造出第一个 18 00:00:44,440 --> 00:00:46,560 在使用信号量来解决 19 00:00:46,600 --> 00:00:48,480 我们的同步互斥问题的时候 20 00:00:48,520 --> 00:00:51,120 我们可以把它分成这样两种情况 21 00:00:51,160 --> 00:00:53,720 一种呢是互斥访问 22 00:00:53,760 --> 00:00:59,240 也就说在用它来控制临界区的互斥 23 00:00:59,280 --> 00:01:02,360 另一个呢是用它来做同步 24 00:01:02,400 --> 00:01:07,560 在两个线程之间实现事件的等待 25 00:01:07,600 --> 00:01:08,800 首先呢我们来看 26 00:01:08,840 --> 00:01:12,080 它具体是如何在做 27 00:01:12,120 --> 00:01:13,240 我们在这里的例子呢 28 00:01:13,280 --> 00:01:16,680 是用信号量实现临界区的互斥访问 29 00:01:16,720 --> 00:01:17,800 这时候做法呢 30 00:01:17,840 --> 00:01:21,480 是用一个信号量 31 00:01:21,520 --> 00:01:23,800 来对应一个临界区 32 00:01:23,840 --> 00:01:25,160 那这时候呢 由于我们说 33 00:01:25,200 --> 00:01:27,680 任何一个临界区在任期一个时刻 34 00:01:27,720 --> 00:01:32,320 只允许一个线程执行临界区的代码 35 00:01:32,360 --> 00:01:35,400 那这时候它的初值是1 36 00:01:35,440 --> 00:01:38,480 好 那我们先是完成它的初始化 37 00:01:38,520 --> 00:01:40,840 在这里呢初值取1 38 00:01:40,880 --> 00:01:42,200 然后我们来看 39 00:01:42,240 --> 00:01:44,360 它如何来控制临界区的访问 40 00:01:44,400 --> 00:01:46,720 那这是它的控制代码 41 00:01:46,760 --> 00:01:48,360 在前面呢有一个P操作 42 00:01:48,400 --> 00:01:50,520 在后面呢有一个V操作 43 00:01:50,560 --> 00:01:54,200 P操作完成申请 V操作完成释放 44 00:01:54,240 --> 00:01:58,080 它怎么来实现这个临界区的互斥呢 45 00:01:58,120 --> 00:02:01,320 首先这个信号量初始值为1 46 00:02:01,360 --> 00:02:03,560 所以第一个线程进来的时候 47 00:02:03,600 --> 00:02:05,240 它由它 把它1变成0 48 00:02:05,280 --> 00:02:07,120 所以它能进去 好 49 00:02:07,160 --> 00:02:09,760 如果说这时候有第二个线程 50 00:02:09,800 --> 00:02:12,120 也想进入临界区 51 00:02:12,160 --> 00:02:14,480 那么刚才第一个还在里头运行 52 00:02:14,520 --> 00:02:16,520 第二个来说它0变成-1 53 00:02:16,560 --> 00:02:17,560 好 那这时候呢 54 00:02:17,600 --> 00:02:20,840 它就在P操作这个地方进入等待状态 55 00:02:20,880 --> 00:02:22,480 好 那这时候 56 00:02:22,520 --> 00:02:25,040 在临界区的第一个线程 57 00:02:25,080 --> 00:02:28,880 它执行结束 进行V操作 58 00:02:28,920 --> 00:02:30,680 那么V操作呢就会导致 59 00:02:30,720 --> 00:02:33,360 它把刚才的这个信号量的计数 60 00:02:33,400 --> 00:02:35,840 由-1改成0 61 00:02:35,880 --> 00:02:37,760 那这时候呢它知道有 62 00:02:37,800 --> 00:02:40,160 第二个线程在里等着呢 63 00:02:40,200 --> 00:02:40,920 好 那么这个时候呢 64 00:02:40,960 --> 00:02:43,520 它唤醒第二个线程 65 00:02:43,560 --> 00:02:44,640 它继续往后走 66 00:02:44,680 --> 00:02:46,000 那这个时候呢 第二个线程 67 00:02:46,040 --> 00:02:49,880 就会在第一个结束的时候进入临界区 68 00:02:49,920 --> 00:02:51,000 那么利用这种做法呢 69 00:02:51,040 --> 00:02:55,280 我们就实现了临界区的互斥访问 70 00:02:55,320 --> 00:02:56,680 那用它来做的时候呢 71 00:02:56,720 --> 00:02:58,400 有几点需要注意的 72 00:02:58,440 --> 00:03:00,880 第一个呢 这里的PV操作 73 00:03:00,920 --> 00:03:03,240 必须成对使用 74 00:03:03,280 --> 00:03:05,200 也就说你有一个申请 75 00:03:05,240 --> 00:03:06,600 有一个释放 76 00:03:06,640 --> 00:03:09,720 如果说 不申请你直接释放 77 00:03:09,760 --> 00:03:10,760 会出现什么情况 78 00:03:10,800 --> 00:03:12,760 就会有多个线程 79 00:03:12,800 --> 00:03:14,600 进入到临界区里头 80 00:03:14,640 --> 00:03:16,880 如果说你只申请不释放 81 00:03:16,920 --> 00:03:19,800 那么这时候呢 就会出现 82 00:03:19,840 --> 00:03:21,440 缓冲区没有任何一个线程 83 00:03:21,480 --> 00:03:24,000 但是谁也进不去的情况 84 00:03:24,040 --> 00:03:25,600 用P操作保证互斥 85 00:03:25,640 --> 00:03:29,240 用V操作保证资源的释放 86 00:03:29,280 --> 00:03:31,360 然后这两个的顺序呢 87 00:03:31,400 --> 00:03:33,160 颠倒或者重复 88 00:03:33,200 --> 00:03:36,400 都会导致可能的麻烦 89 00:03:36,440 --> 00:03:38,240 第二个呢 我们用信号量 90 00:03:38,280 --> 00:03:40,760 来实现条件同步 91 00:03:40,800 --> 00:03:44,880 那这时候呢也是用一个信号量 92 00:03:44,920 --> 00:03:47,880 来描述一个条件同步 93 00:03:47,920 --> 00:03:50,040 那它的初值是0 94 00:03:50,080 --> 00:03:51,680 也就说在起头的时候 95 00:03:51,720 --> 00:03:52,840 那个事件还没出现 96 00:03:52,880 --> 00:03:54,640 出现之后 那这对于我们来说 97 00:03:54,680 --> 00:03:56,840 是一个资源 它就变成1了 98 00:03:56,880 --> 00:03:58,400 好 等待资源你申请 99 00:03:58,440 --> 00:03:59,680 那这时候事件出现之后 100 00:03:59,720 --> 00:04:02,840 那等待的就可以继续往下走 101 00:04:02,880 --> 00:04:04,440 具体怎么做呢 102 00:04:04,480 --> 00:04:08,080 初值它设成为0 103 00:04:08,120 --> 00:04:10,920 我有两个线程A和B 104 00:04:10,960 --> 00:04:16,400 分别有mn和xy各自的两个模块 105 00:04:16,440 --> 00:04:17,840 那我们需要做的事情呢 106 00:04:17,880 --> 00:04:20,920 是在B执行到x之后 107 00:04:20,960 --> 00:04:24,000 这个A才能执行n模块 108 00:04:24,040 --> 00:04:27,200 比如说我在这里这边要准备数据 109 00:04:27,240 --> 00:04:28,680 这边是使用数据 110 00:04:28,720 --> 00:04:31,000 如果说你的数据没准备好 111 00:04:31,040 --> 00:04:33,000 那你要想使用数据 112 00:04:33,040 --> 00:04:34,360 比如说n模块是 113 00:04:34,400 --> 00:04:35,760 对数据进行处理的话 114 00:04:35,800 --> 00:04:37,600 那你就没有可以处理的数据 115 00:04:37,640 --> 00:04:38,640 它们俩之间呢 116 00:04:38,680 --> 00:04:41,560 必须等到B执行完x 117 00:04:41,600 --> 00:04:43,560 A才能执行n 118 00:04:43,600 --> 00:04:45,080 好 那这时候我们怎么办呢 119 00:04:45,120 --> 00:04:49,040 在这里呢 一个P操作一个V操作 120 00:04:49,080 --> 00:04:53,160 在B里头 释放信号量的 121 00:04:53,200 --> 00:04:57,840 好 那么这时候它就会由0变成1 122 00:04:57,880 --> 00:05:01,440 好 如果说进程B 123 00:05:01,480 --> 00:05:04,880 先执行完x 到达这个点 124 00:05:04,920 --> 00:05:05,960 那么这个时候呢 125 00:05:06,000 --> 00:05:07,720 A执行完m的时候 126 00:05:07,760 --> 00:05:09,640 这个地方信号量的值就是1了 127 00:05:09,680 --> 00:05:12,120 好 它就直接能往下执行了 128 00:05:12,160 --> 00:05:14,280 假定不是这种情况 129 00:05:14,320 --> 00:05:16,800 是A先执行完m 130 00:05:16,840 --> 00:05:18,800 那么这时候呢它来申请 131 00:05:18,840 --> 00:05:20,320 进行P操作 132 00:05:20,360 --> 00:05:22,920 这时候呢信号量的初值是0 133 00:05:22,960 --> 00:05:24,040 那么这时候它变成-1 134 00:05:24,080 --> 00:05:26,000 这时候它堵塞 进入等待状态 135 00:05:26,040 --> 00:05:28,400 好 然后这个进程B呢 136 00:05:28,440 --> 00:05:30,720 执行完x到这儿它释放 137 00:05:30,760 --> 00:05:32,960 释放的时候我们刚刚还记得 138 00:05:33,000 --> 00:05:36,120 信号量的V操作的实现里头 139 00:05:36,160 --> 00:05:38,600 它是加一 如果说加完之后 140 00:05:38,640 --> 00:05:41,280 它是小于等于0的 141 00:05:41,320 --> 00:05:42,360 那这时候知道 142 00:05:42,400 --> 00:05:44,840 有另外一个线程A在等待 143 00:05:44,880 --> 00:05:47,720 好 那这时候唤醒A 它继续往后走 144 00:05:47,760 --> 00:05:49,240 这时候线程A呢 145 00:05:49,280 --> 00:05:51,000 也可以继续往下走了 146 00:05:51,040 --> 00:05:52,600 好 所以从这个地方呢 147 00:05:52,640 --> 00:05:54,600 我们就实现了一个条件等待 148 00:05:54,640 --> 00:05:59,480 线程A要等待线程B执行完x模块 149 00:05:59,520 --> 00:06:02,120 它A才能执行n模块 150 00:06:02,160 --> 00:06:04,640 这是我们用信号量 151 00:06:04,680 --> 00:06:08,720 来实现同步的两个基本的情况 152 00:06:08,760 --> 00:06:11,480 和我们这里的基本做法 153 00:06:11,520 --> 00:06:12,480 好 那我们下面呢 154 00:06:12,520 --> 00:06:15,680 来看一个更实际的例子 155 00:06:15,720 --> 00:06:17,080 就是这里我们所说的 156 00:06:17,120 --> 00:06:20,000 生产者 消费者问题 157 00:06:20,040 --> 00:06:24,280 这个问题的基本要求呢是这样的 158 00:06:24,320 --> 00:06:27,360 有一组生产者 一个或者是多个 159 00:06:27,400 --> 00:06:29,240 它们负责产生数据 160 00:06:29,280 --> 00:06:32,040 并且把这数据呢放在缓冲区里头 161 00:06:32,080 --> 00:06:37,240 然后另外呢还有一个消费者 162 00:06:37,280 --> 00:06:38,280 这个消费者呢 163 00:06:38,320 --> 00:06:41,040 负责从缓冲区里读数据 164 00:06:41,080 --> 00:06:43,000 进行后续的处理 165 00:06:43,040 --> 00:06:45,440 比如说我们在这里呢 166 00:06:45,480 --> 00:06:47,880 消费者就是对应我们打印进程 167 00:06:47,920 --> 00:06:50,960 前面呢就是对应着你的应用程序 168 00:06:51,000 --> 00:06:52,320 若干个应用程序 169 00:06:52,360 --> 00:06:54,040 会产生打印作业 170 00:06:54,080 --> 00:06:55,800 放在缓冲区里头 171 00:06:55,840 --> 00:06:57,040 打印服务器呢 172 00:06:57,080 --> 00:06:58,440 负责从缓冲区里 173 00:06:58,480 --> 00:07:00,120 读出你的打印数据 174 00:07:00,160 --> 00:07:01,760 然后进行打印 175 00:07:01,800 --> 00:07:04,680 那这个缓冲区呢有一个限制 176 00:07:04,720 --> 00:07:06,280 就是在任何时刻 177 00:07:06,320 --> 00:07:10,440 只能有一个生产者或者是消费者 178 00:07:10,480 --> 00:07:12,200 可以访问这个缓冲区 179 00:07:12,240 --> 00:07:14,160 不可以两个同时来做 180 00:07:14,200 --> 00:07:16,520 那它们在用的过程当中 181 00:07:16,560 --> 00:07:18,400 可能会有这种情况 182 00:07:18,440 --> 00:07:21,240 生产者往缓冲区里写 183 00:07:21,280 --> 00:07:23,400 那我的缓冲区得有空地才能写 184 00:07:23,440 --> 00:07:26,440 如果写满了 那它做不下去了 185 00:07:26,480 --> 00:07:28,360 消费者从里读数据 186 00:07:28,400 --> 00:07:30,160 里头得有数据它才能读 187 00:07:30,200 --> 00:07:32,760 如果没有数据它得等着 188 00:07:32,800 --> 00:07:34,560 好 我们就来看 189 00:07:34,600 --> 00:07:36,280 如何用信号量实现 190 00:07:36,320 --> 00:07:41,000 生产者 消费者之间的协调关系 191 00:07:41,040 --> 00:07:43,560 我们如何用信号量来解决它呢 192 00:07:43,600 --> 00:07:46,360 我们首先对这个问题呢做一些分析 193 00:07:46,400 --> 00:07:48,320 从我们刚才的描述里呢 194 00:07:48,360 --> 00:07:49,800 大家可以知道 195 00:07:49,840 --> 00:07:52,640 实际上这个缓冲区是一个临界区 196 00:07:52,680 --> 00:07:54,160 任何一个时刻 197 00:07:54,200 --> 00:07:57,560 只允许一个线程对缓冲区进行操作 198 00:07:57,600 --> 00:08:00,320 这可以描述成临界区 199 00:08:00,360 --> 00:08:03,040 除此之外呢还有另外两条限制 200 00:08:03,080 --> 00:08:05,000 第一条呢是说 201 00:08:05,040 --> 00:08:07,840 消费者要从缓冲区里读数据 202 00:08:07,880 --> 00:08:11,000 如果说缓冲区里没有数据 是空的 203 00:08:11,040 --> 00:08:13,200 我消费者是没办法进行下去的 204 00:08:13,240 --> 00:08:15,480 它必须等待生产者 205 00:08:15,520 --> 00:08:17,600 往缓冲区里放了数据之后 206 00:08:17,640 --> 00:08:20,720 它才能够继续往后进行操作 207 00:08:20,760 --> 00:08:23,160 另一个呢是生产者 208 00:08:23,200 --> 00:08:26,720 生产者必须等待缓冲区里头有空地 209 00:08:26,760 --> 00:08:28,400 你才能够往里放 210 00:08:28,440 --> 00:08:31,920 如果缓冲区满了 那生产者必须等待 211 00:08:31,960 --> 00:08:34,560 我们把生产者消费者问题的描述呢 212 00:08:34,600 --> 00:08:37,680 转换成了我们用临界区 213 00:08:37,720 --> 00:08:39,640 和条件等待的方式 214 00:08:39,680 --> 00:08:41,000 给它描述出来 215 00:08:41,040 --> 00:08:42,960 那这三条呢 对应过来就是 216 00:08:43,000 --> 00:08:46,400 我们这里的互斥访问 缓冲区 217 00:08:46,440 --> 00:08:49,240 然后生产者和消费者 218 00:08:49,280 --> 00:08:51,160 分别有一个条件等待 219 00:08:51,200 --> 00:08:53,120 是等待对方的 220 00:08:53,160 --> 00:08:55,360 那这时候呢 我们把它描述出来 221 00:08:55,400 --> 00:09:00,760 就变成这样几个信号量所做的约束 222 00:09:00,800 --> 00:09:02,760 第一个呢是二进制信号量 223 00:09:02,800 --> 00:09:05,320 它描述这里的互斥关系 224 00:09:05,360 --> 00:09:07,640 第二个就是fullBeffers 225 00:09:07,680 --> 00:09:11,400 它对应着 有数据我才能够往外读 226 00:09:11,440 --> 00:09:15,960 所以消费者必须等待缓冲区里有数据 227 00:09:16,000 --> 00:09:20,440 然后再有一个呢 是生产者 228 00:09:20,480 --> 00:09:23,640 生产者必须等待缓冲区有空地 229 00:09:23,680 --> 00:09:25,040 你才能往里写 230 00:09:25,080 --> 00:09:27,640 好 这两者对应这两个信号量 231 00:09:27,680 --> 00:09:28,720 它加起来之后呢 232 00:09:28,760 --> 00:09:31,680 应该是你缓冲区的总的大小 233 00:09:31,720 --> 00:09:33,560 好 有了这些分析之后呢 234 00:09:33,600 --> 00:09:35,800 我们基本上就把我们这里的 235 00:09:35,840 --> 00:09:38,760 生产者消费者这一个同步的问题 236 00:09:38,800 --> 00:09:40,040 转换成我们刚才说的 237 00:09:40,080 --> 00:09:42,640 互斥访问和条件等待 238 00:09:42,680 --> 00:09:43,440 那接下来呢 239 00:09:43,480 --> 00:09:46,320 我们把它转换成实际的代码 240 00:09:46,360 --> 00:09:49,040 首先呢 是对它的初始化 241 00:09:49,080 --> 00:09:50,160 二进制信号量 242 00:09:50,200 --> 00:09:52,720 它的初值是一个临界区 初值为1 243 00:09:52,760 --> 00:09:54,440 好 然后刚开始的时候 244 00:09:54,480 --> 00:09:56,360 缓冲区里没有任何数据 245 00:09:56,400 --> 00:09:57,880 所以缓冲区满 246 00:09:57,920 --> 00:09:59,880 这个地方它的初值是0 247 00:09:59,920 --> 00:10:02,720 好 然后说有多少个空缓冲区呢 248 00:10:02,760 --> 00:10:05,920 每个空缓冲区对应着一个资源 249 00:10:05,960 --> 00:10:08,680 生产者 消费者它是如何在做 250 00:10:08,720 --> 00:10:12,320 这是生产者 这是消费者 251 00:10:12,360 --> 00:10:14,200 它们对应的代码 252 00:10:14,240 --> 00:10:16,880 那在这里呢 往缓冲区里放数据 253 00:10:16,920 --> 00:10:18,200 从缓冲区里读数据 254 00:10:18,240 --> 00:10:20,320 是它们要进行的主要操作 255 00:10:20,360 --> 00:10:22,640 那首先第一个 256 00:10:22,680 --> 00:10:25,440 我们要实现这个是互斥访问临界区 257 00:10:25,480 --> 00:10:26,560 所以在这儿呢 258 00:10:26,600 --> 00:10:28,240 有一个P操作 有一个V操作 259 00:10:28,280 --> 00:10:31,600 是申请这个二进制信号量mutex 260 00:10:31,640 --> 00:10:34,480 好 在这边呢也是一样 261 00:10:34,520 --> 00:10:35,960 好 实现他们俩之间 262 00:10:36,000 --> 00:10:38,720 对缓冲区的互斥访问 263 00:10:38,760 --> 00:10:40,600 它仅有互斥是不够的 264 00:10:40,640 --> 00:10:43,200 如果说缓冲区里头没有空地 265 00:10:43,240 --> 00:10:45,600 你即使申请到了缓冲区的互斥操作 266 00:10:45,640 --> 00:10:47,120 你也没办法操作 267 00:10:47,160 --> 00:10:48,400 因为里头没空地了 268 00:10:48,440 --> 00:10:50,760 而这时候你又把缓冲区占掉了 269 00:10:50,800 --> 00:10:53,600 消费者已经没有办法帮你腾出空地来 270 00:10:53,640 --> 00:10:55,240 所以在这儿之前呢 271 00:10:55,280 --> 00:10:59,880 我需要来检查是否有空缓冲区 272 00:10:59,920 --> 00:11:02,280 有 我可以再进一步检查 273 00:11:02,320 --> 00:11:04,920 是否有另外一个生产者或者消费者 274 00:11:04,960 --> 00:11:07,160 在访问缓冲区 275 00:11:07,200 --> 00:11:08,160 好 如果没有 276 00:11:08,200 --> 00:11:10,080 那它就可以往里放数据了 277 00:11:10,120 --> 00:11:11,680 那这一条有没有缓冲区 278 00:11:11,720 --> 00:11:14,000 它在什么时候会去释放呢 279 00:11:14,040 --> 00:11:18,600 那消费者在从里头读出数据之后 280 00:11:18,640 --> 00:11:21,800 它在这里呢会释放一个空缓冲区资源 281 00:11:21,840 --> 00:11:25,240 也就相当于我给你腾出一个空地来 282 00:11:25,280 --> 00:11:27,400 而另一头呢 283 00:11:27,440 --> 00:11:29,040 消费者要想进入的时候呢 284 00:11:29,080 --> 00:11:31,480 它得看里头是否有数据 285 00:11:31,520 --> 00:11:33,320 好 在这个地方呢 负 286 00:11:33,360 --> 00:11:38,720 那实际上就是我看里头是否有数据 287 00:11:38,760 --> 00:11:40,800 那只有生产者往里写了数据之后 288 00:11:40,840 --> 00:11:44,840 我这个地方呢才能有数据资源 289 00:11:44,880 --> 00:11:47,880 好 那你才可以从里头读数据 290 00:11:47,920 --> 00:11:50,920 生产者在往里写了数据之后 291 00:11:50,960 --> 00:11:53,760 它就释放一个fullBuffer 292 00:11:53,800 --> 00:11:54,960 这样一个资源 293 00:11:55,000 --> 00:11:56,000 释放这个资源之后 294 00:11:56,040 --> 00:11:57,800 实际相当于我这个里头就多了数据 295 00:11:57,840 --> 00:11:59,760 好 这时候这个消费者呢 296 00:11:59,800 --> 00:12:02,200 他们俩是配合 297 00:12:02,240 --> 00:12:03,480 这时候有一个问题 298 00:12:03,520 --> 00:12:08,240 我在这个地方这个顺序可以调整吗 299 00:12:08,280 --> 00:12:09,320 在这里头 如果说 300 00:12:09,360 --> 00:12:11,080 你把这两个顺序调整了 301 00:12:11,120 --> 00:12:13,120 它就会出现死锁 302 00:12:13,160 --> 00:12:17,560 原因是在于我检查空和满 303 00:12:17,600 --> 00:12:18,720 你需要先检查 304 00:12:18,760 --> 00:12:21,400 然后我再去申请互斥访问 305 00:12:21,440 --> 00:12:23,720 如果说你申请了互斥访问 306 00:12:23,760 --> 00:12:26,080 那这个时候你已经占用了临界资源 307 00:12:26,120 --> 00:12:27,640 那别人不能读写了 308 00:12:27,680 --> 00:12:29,320 而这时候你又发现里头是满 309 00:12:29,360 --> 00:12:30,880 或者发现里头是空 310 00:12:30,920 --> 00:12:32,720 那这时候你进行不下去了 311 00:12:32,760 --> 00:12:35,240 但这时候呢别人也没有办法 312 00:12:35,280 --> 00:12:37,000 来申请到这个临界区 313 00:12:37,040 --> 00:12:40,320 那这时候它就会形成死锁 314 00:12:40,360 --> 00:12:43,400 这是用信号量机制 315 00:12:43,440 --> 00:12:46,840 来解决生产者消费者问题 316 00:12:46,880 --> 00:12:49,240 从这个应用的例子当中我们可以看到 317 00:12:49,280 --> 00:12:52,040 实际上用信号量来解决同步问题 318 00:12:52,080 --> 00:12:55,080 它是需要你有技巧的 319 00:12:55,120 --> 00:12:56,680 程序员在写的时候 320 00:12:56,720 --> 00:12:59,320 你必须很好的了解你的问题 321 00:12:59,360 --> 00:13:01,840 和很好的运用信号量机制 322 00:13:01,880 --> 00:13:03,840 并且这时候呢 323 00:13:03,880 --> 00:13:05,680 它是容易出错的 324 00:13:05,720 --> 00:13:07,440 我可能在某个地方 325 00:13:07,480 --> 00:13:08,920 忘了一个P操作 326 00:13:08,960 --> 00:13:10,880 或者忘了一个V操作 327 00:13:10,920 --> 00:13:13,480 或者说我把它的顺序搞倒了 328 00:13:13,520 --> 00:13:15,640 这些呢都是容易出问题的 329 00:13:15,680 --> 00:13:19,440 所以它没有办法避免死锁的出现 330 00:13:19,480 --> 00:13:21,320 你必须在你写程序的时候 331 00:13:21,360 --> 00:13:22,840 来解决这个问题 332 00:13:22,880 --> 00:13:22,920