0 00:00:00,000 --> 00:00:16,320 1 00:00:16,360 --> 00:00:20,240 这一讲我们来介绍信号量和管程 2 00:00:20,280 --> 00:00:21,520 信号量和管程 3 00:00:21,560 --> 00:00:22,960 都是操作系统当中 4 00:00:23,000 --> 00:00:26,120 提供的两种同步方法 5 00:00:26,160 --> 00:00:27,880 那最开始我们说 6 00:00:27,920 --> 00:00:29,360 因为我们在系统里呢 7 00:00:29,400 --> 00:00:31,440 为了提高系统的并发性 8 00:00:31,480 --> 00:00:35,160 所以我们引入了多进程和多线程 9 00:00:35,200 --> 00:00:37,240 多线程的引入导致了 10 00:00:37,280 --> 00:00:40,520 资源的共享使用和竞争 11 00:00:40,560 --> 00:00:41,800 我们就会需要去 12 00:00:41,840 --> 00:00:43,720 寻找解决的办法 13 00:00:43,760 --> 00:00:45,440 这就是我们这里要同步 14 00:00:45,480 --> 00:00:47,600 同步呢是为了协调多个进程 15 00:00:47,640 --> 00:00:50,440 对共享数据的访问 16 00:00:50,480 --> 00:00:52,040 我们把这个问题抽象出来 17 00:00:52,080 --> 00:00:54,240 就变成是在任何一个时刻 18 00:00:54,280 --> 00:00:56,440 那我们只能有一个线程 19 00:00:56,480 --> 00:00:58,640 在执行临界区的代码 20 00:00:58,680 --> 00:01:00,000 解决的办法呢 21 00:01:00,040 --> 00:01:01,840 我们前面有这样几种说 22 00:01:01,880 --> 00:01:05,560 我们可以基于底层的硬件实现 23 00:01:05,600 --> 00:01:06,720 也可以在上面呢 24 00:01:06,760 --> 00:01:10,240 有高层的抽象编程方法 25 00:01:10,280 --> 00:01:13,200 那我们今天讲的信号量和管程 26 00:01:13,240 --> 00:01:17,720 都是属于编程方法的两种 27 00:01:17,760 --> 00:01:20,640 这是我们要最终解决的问题 28 00:01:20,680 --> 00:01:23,240 我们需要协调各个线程 29 00:01:23,280 --> 00:01:26,240 对临界资源的访问 30 00:01:26,280 --> 00:01:29,480 然后我们的做法呢有这样几种 31 00:01:29,520 --> 00:01:31,320 首先呢我们在最底层 32 00:01:31,360 --> 00:01:32,520 希望来解决这个问题 33 00:01:32,560 --> 00:01:34,720 你比如说禁止中断 34 00:01:34,760 --> 00:01:38,840 那我也有 做原子操作 35 00:01:38,880 --> 00:01:41,400 把读和修改这两个 36 00:01:41,440 --> 00:01:42,680 变成一个原子操作 37 00:01:42,720 --> 00:01:44,520 比如说我们TS指令 38 00:01:44,560 --> 00:01:46,400 然后我们也有呢 39 00:01:46,440 --> 00:01:49,360 完全基于最基本的读写操作 40 00:01:49,400 --> 00:01:52,200 来构造出来的软件同步办法 41 00:01:52,240 --> 00:01:53,480 这几类办法呢 42 00:01:53,520 --> 00:01:56,120 我们把它都抽象成一个接口 43 00:01:56,160 --> 00:01:58,320 说 就是我们这里前面说的锁 44 00:01:58,360 --> 00:02:01,000 那用锁来申请临界区的进入 45 00:02:01,040 --> 00:02:04,840 和在临界区完成之后那退出释放 46 00:02:04,880 --> 00:02:07,800 好 这个机制能比较好地抽象 47 00:02:07,840 --> 00:02:09,640 而它的实现呢 48 00:02:09,680 --> 00:02:13,600 是依靠这底下这三种办法 49 00:02:13,640 --> 00:02:16,480 这三种办法呢各有各的适用场合 50 00:02:16,520 --> 00:02:18,600 然后有一个统一的锁机制之后 51 00:02:18,640 --> 00:02:22,240 我们在上面能实现临界区的访问 52 00:02:22,280 --> 00:02:23,880 今天我们要说的信号量呢 53 00:02:23,920 --> 00:02:25,720 实际上就是跟锁机制 54 00:02:25,760 --> 00:02:28,920 在同一个层次上的一种编程方法 55 00:02:28,960 --> 00:02:31,600 它的做法是在操作系统的控制下 56 00:02:31,640 --> 00:02:33,520 来做这件事情 57 00:02:33,560 --> 00:02:36,280 信号量是操作系统提供的一种 58 00:02:36,320 --> 00:02:39,640 协调共享资源访问的方法 59 00:02:39,680 --> 00:02:41,000 那这种方法呢 60 00:02:41,040 --> 00:02:43,360 它和我们前面讲到的 61 00:02:43,400 --> 00:02:45,800 软件同步方法有什么区别呢 62 00:02:45,840 --> 00:02:47,040 软件同步方法呢 63 00:02:47,080 --> 00:02:50,040 实际上我们讨论的是平等的 64 00:02:50,080 --> 00:02:53,480 线程之间的一种协调机制 65 00:02:53,520 --> 00:02:54,920 那也就是说我如果用线程 66 00:02:54,960 --> 00:02:57,000 通过软件方法来协调 67 00:02:57,040 --> 00:02:58,520 谁进入临界区的时候 68 00:02:58,560 --> 00:03:02,040 这些线程之间的位置是完全平等的 69 00:03:02,080 --> 00:03:03,920 但是我们这里讲到的信号量呢 70 00:03:03,960 --> 00:03:08,200 实际上它是由操作系统来负责管理的 71 00:03:08,240 --> 00:03:09,800 操作系统作为管理者 72 00:03:09,840 --> 00:03:12,000 它高于我们的进程 73 00:03:12,040 --> 00:03:13,160 这做一个类比 74 00:03:13,200 --> 00:03:15,720 就相当于是说我们在球场上踢球 75 00:03:15,760 --> 00:03:18,680 一种是说两个球队一块踢 76 00:03:18,720 --> 00:03:19,800 没有裁判 77 00:03:19,840 --> 00:03:20,520 好 那这时候 78 00:03:20,560 --> 00:03:21,960 我们有一套共同的规则 79 00:03:22,000 --> 00:03:23,680 好 那这时候大家一起来踢 80 00:03:23,720 --> 00:03:24,640 在这个过程当中呢 81 00:03:24,680 --> 00:03:26,960 如果说有问题 我们照规则来 82 00:03:27,000 --> 00:03:28,200 但实际上这时候呢 83 00:03:28,240 --> 00:03:29,160 大家对规则理解 84 00:03:29,200 --> 00:03:31,560 总是会有各种各样偏差 85 00:03:31,600 --> 00:03:33,760 于是我们引入一个裁判 86 00:03:33,800 --> 00:03:34,920 那裁判就是说 87 00:03:34,960 --> 00:03:36,240 只要我们俩有分歧的时候 88 00:03:36,280 --> 00:03:37,560 找到一个仲裁 89 00:03:37,600 --> 00:03:38,840 由裁判说了算 90 00:03:38,880 --> 00:03:40,080 那我们在这儿呢 91 00:03:40,120 --> 00:03:42,160 实际上就是由操作系统 92 00:03:42,200 --> 00:03:43,640 来做这个裁判 93 00:03:43,680 --> 00:03:44,880 也就说两个进程协调 94 00:03:44,920 --> 00:03:47,280 谁能使用临界区资源 95 00:03:47,320 --> 00:03:50,080 由操作系统说了算 96 00:03:50,120 --> 00:03:51,280 有了一个仲裁者 97 00:03:51,320 --> 00:03:52,720 或者一个管理者之后呢 98 00:03:52,760 --> 00:03:55,000 这种协调会变得比原来容易 99 00:03:55,040 --> 00:03:56,080 好 在这种情况下 100 00:03:56,120 --> 00:03:58,280 我们用一个信号量 101 00:03:58,320 --> 00:04:00,800 来表示一类资源 102 00:04:00,840 --> 00:04:02,120 信号量的取值呢 103 00:04:02,160 --> 00:04:04,480 就是它的资源的数目 104 00:04:04,520 --> 00:04:06,640 这种做法呢是最早 105 00:04:06,680 --> 00:04:08,200 由一个荷兰科学家 106 00:04:08,240 --> 00:04:10,440 叫Dijkstra 他提出的 107 00:04:10,480 --> 00:04:11,440 那这个名字呢 108 00:04:11,480 --> 00:04:13,080 我们在前面的数据结构里头 109 00:04:13,120 --> 00:04:14,000 计算机科学里头 110 00:04:14,040 --> 00:04:16,040 有很多他做出的贡献 111 00:04:16,080 --> 00:04:19,040 这种办法呢在早期的操作系统当中 112 00:04:19,080 --> 00:04:20,920 是最主要的同步机制 113 00:04:20,960 --> 00:04:23,040 那目前呢在我们实际系统当中 114 00:04:23,080 --> 00:04:25,000 这种用的相对来说比较少 115 00:04:25,040 --> 00:04:29,040 但是它的重要性呢仍然是非常高的 116 00:04:29,080 --> 00:04:31,600 它具体怎么做呢 117 00:04:31,640 --> 00:04:34,600 信号量是一种抽象的数据类型 118 00:04:34,640 --> 00:04:38,800 这个数据类型里头有三个内容 119 00:04:38,840 --> 00:04:40,880 一个是一个整型变量 120 00:04:40,920 --> 00:04:42,080 这个变量的取值呢 121 00:04:42,120 --> 00:04:43,200 你可以理解为 122 00:04:43,240 --> 00:04:46,280 你要共享的资源的数目 123 00:04:46,320 --> 00:04:49,240 然后由两个原子操作 124 00:04:49,280 --> 00:04:52,600 那这两个原子操作呢一个叫P操作 125 00:04:52,640 --> 00:04:55,680 它是申请资源的时候要用的 126 00:04:55,720 --> 00:04:57,880 我申请使用一个资源 127 00:04:57,920 --> 00:04:59,760 那我把这个计数呢减一 128 00:04:59,800 --> 00:05:02,120 也就相当于可用的资源数变少了 129 00:05:02,160 --> 00:05:03,080 好 那这时候呢 130 00:05:03,120 --> 00:05:04,880 就可能有一种情况是说 131 00:05:04,920 --> 00:05:06,800 我来申请资源的时候 132 00:05:06,840 --> 00:05:08,520 已经没有资源了 133 00:05:08,560 --> 00:05:10,080 那这时候我就减成一个负数了 134 00:05:10,120 --> 00:05:11,240 减成负数之后呢 135 00:05:11,280 --> 00:05:13,840 我就需要等待另外的线程 136 00:05:13,880 --> 00:05:15,320 用完这个资源之后 137 00:05:15,360 --> 00:05:16,720 它释放了我才能用 138 00:05:16,760 --> 00:05:19,240 好 那这时候呢它就进入等待状态 139 00:05:19,280 --> 00:05:21,400 如果说不是这样 它是大于0的 140 00:05:21,440 --> 00:05:23,040 也就相当于我拿到了相应资源 141 00:05:23,080 --> 00:05:24,320 那我就可以使用了 142 00:05:24,360 --> 00:05:27,200 好 这个P的出处呢是荷兰语 143 00:05:27,240 --> 00:05:29,440 那最早这个科学家他做出来的 144 00:05:29,480 --> 00:05:33,040 我们后续就延续这个提法了 145 00:05:33,080 --> 00:05:34,720 然后反过来呢 146 00:05:34,760 --> 00:05:36,080 我用完资源之后 147 00:05:36,120 --> 00:05:38,000 我释放的时候一个V操作 148 00:05:38,040 --> 00:05:40,280 V操作就是把计数加一 149 00:05:40,320 --> 00:05:43,000 如果说你占用的是 150 00:05:43,040 --> 00:05:45,960 由1变成0的那个资源 151 00:05:46,000 --> 00:05:47,600 后面的线程再来的时候 152 00:05:47,640 --> 00:05:49,600 那它就已经是负数了 它在等待 153 00:05:49,640 --> 00:05:52,280 好 那这时候你加一之后 154 00:05:52,320 --> 00:05:55,240 如果说它仍然是小于等于0的 155 00:05:55,280 --> 00:05:56,160 那么这时候就说明 156 00:05:56,200 --> 00:05:58,160 有另外一个线程 157 00:05:58,200 --> 00:06:00,040 在等这个资源来使用 158 00:06:00,080 --> 00:06:01,600 它目前处于等待状态 159 00:06:01,640 --> 00:06:02,760 好 那这时候呢 160 00:06:02,800 --> 00:06:07,600 我就唤醒相应的等待线程 161 00:06:07,640 --> 00:06:08,840 好 这个V呢 162 00:06:08,880 --> 00:06:10,360 也是跟P一样的 163 00:06:10,400 --> 00:06:13,520 它来自于荷兰语 164 00:06:13,560 --> 00:06:14,960 好 有了这两个之后 165 00:06:15,000 --> 00:06:17,160 我们看这个它怎么来用的 166 00:06:17,200 --> 00:06:18,800 我们做这样一个类比 167 00:06:18,840 --> 00:06:21,920 也就说我假定有一个车站 168 00:06:21,960 --> 00:06:23,560 车站有两个站台 169 00:06:23,600 --> 00:06:25,520 一个站台呢可以停一列火车 170 00:06:25,560 --> 00:06:28,640 进行上下乘客 或者说货物的装卸 171 00:06:28,680 --> 00:06:30,480 好 我们在这儿呢把它抽象成 172 00:06:30,520 --> 00:06:33,400 一个有两个资源的信号量 173 00:06:33,440 --> 00:06:34,520 它做法什么样子呢 174 00:06:34,560 --> 00:06:35,520 在这儿我们看到 175 00:06:35,560 --> 00:06:38,280 这是车站的两个站台 176 00:06:38,320 --> 00:06:40,720 好 没有任何站台被使用的时候 177 00:06:40,760 --> 00:06:42,680 这时候任何一个火车 178 00:06:42,720 --> 00:06:43,640 都是可以进来的 179 00:06:43,680 --> 00:06:45,480 好 那这时候呢有一列火车 180 00:06:45,520 --> 00:06:47,040 直接进到一个站台 181 00:06:47,080 --> 00:06:49,120 然后这时候仍然有资源呢 182 00:06:49,160 --> 00:06:51,800 这时候我这儿还是绿灯 183 00:06:51,840 --> 00:06:53,920 好 等我把这资源占满了 184 00:06:53,960 --> 00:06:55,200 我们通常情况下理解 185 00:06:55,240 --> 00:06:56,080 这时候就有红灯 186 00:06:56,120 --> 00:06:57,640 后面再来火车的话 187 00:06:57,680 --> 00:06:59,320 它就停到这儿了 188 00:06:59,360 --> 00:07:01,760 好 那这时候呢说再来火车 189 00:07:01,800 --> 00:07:02,600 那实际上这时候呢 我就相当于 190 00:07:02,640 --> 00:07:06,440 这地方的P操作减1再减1 191 00:07:06,480 --> 00:07:08,800 这时候由2就减成0了 192 00:07:08,840 --> 00:07:09,720 好 那这时候呢 193 00:07:09,760 --> 00:07:11,000 再来第三辆的时候 194 00:07:11,040 --> 00:07:12,480 它就出去等着了 195 00:07:12,520 --> 00:07:16,520 等第一辆火车装卸完毕 它离开 196 00:07:16,560 --> 00:07:17,760 好 那这时候加1 197 00:07:17,800 --> 00:07:21,080 加1之后发现那 198 00:07:21,120 --> 00:07:23,360 我这里头后面还有一个等着的 199 00:07:23,400 --> 00:07:27,120 那这时候呢它就可以进来了 200 00:07:27,160 --> 00:07:29,600 好 然后这时候呢两个都占用了 201 00:07:29,640 --> 00:07:33,720 那我们在信号灯上看着就是红灯了 202 00:07:33,760 --> 00:07:35,920 那我们实际上信号量的作用呢 203 00:07:35,960 --> 00:07:41,080 就是类似于这个车站的控制流程 204 00:07:41,120 --> 00:07:43,080 好 那我们说信号量它是 205 00:07:43,120 --> 00:07:46,600 由一个整型变量加两个操作 206 00:07:46,640 --> 00:07:48,280 那么这个整型变量呢 207 00:07:48,320 --> 00:07:50,640 加上前面这个操作呢是 208 00:07:50,680 --> 00:07:53,200 P操作和V操作对它进行保护 209 00:07:53,240 --> 00:07:54,880 好这种保护之后呢 210 00:07:54,920 --> 00:07:56,280 实际上我们就能做到 211 00:07:56,320 --> 00:07:59,600 你对信号量中的整型变量的修改 212 00:07:59,640 --> 00:08:02,360 你只能通过PV操作来完成 213 00:08:02,400 --> 00:08:04,000 操作系统呢来保证 214 00:08:04,040 --> 00:08:06,720 这个PV操作的原子性 215 00:08:06,760 --> 00:08:08,840 那实际上这是我们前面说到的 216 00:08:08,880 --> 00:08:11,280 操作系统在执行它的代码的时候 217 00:08:11,320 --> 00:08:13,320 它的优先级高于我们 218 00:08:13,360 --> 00:08:14,640 进程的用户代码 219 00:08:14,680 --> 00:08:15,480 所以在这时候呢 220 00:08:15,520 --> 00:08:17,440 它能保证它在执行过程当中 221 00:08:17,480 --> 00:08:21,280 不受应用进程的代码执行的干扰 222 00:08:21,320 --> 00:08:24,560 好 那这样就能保证它的原子性了 223 00:08:24,600 --> 00:08:27,360 好 那这时候在执行过程当中呢 224 00:08:27,400 --> 00:08:29,400 P操作有可能由于没有资源 225 00:08:29,440 --> 00:08:31,520 而进入等待状态 226 00:08:31,560 --> 00:08:33,560 V操作呢只会释放资源 227 00:08:33,600 --> 00:08:35,400 处于等待状态的进程呢 228 00:08:35,440 --> 00:08:38,960 变成是就绪状态 229 00:08:39,000 --> 00:08:40,200 好 那这时候说 230 00:08:40,240 --> 00:08:43,120 我们用信号量实现的同步 231 00:08:43,160 --> 00:08:46,160 那这时候它能够做到公平吗 232 00:08:46,200 --> 00:08:48,520 在我们实际用的时候呢 233 00:08:48,560 --> 00:08:51,760 通常情况下假设它是公平的 234 00:08:51,800 --> 00:08:53,040 在实际的系统当中呢 235 00:08:53,080 --> 00:08:55,760 这个公平呢也会有些偏差的 236 00:08:55,800 --> 00:08:58,160 那我们在这里说到的公平是指 237 00:08:58,200 --> 00:09:01,640 我线程不会无限期的等待下去 238 00:09:01,680 --> 00:09:02,880 它一定会结束 239 00:09:02,920 --> 00:09:03,760 这是由于操作系统 240 00:09:03,800 --> 00:09:05,840 在这里起的作用 241 00:09:05,880 --> 00:09:07,760 那实际上我们在实际系统用的时候 242 00:09:07,800 --> 00:09:10,560 这个P操作呢它也会说 243 00:09:10,600 --> 00:09:14,040 在后面有一个等的最长时限的参数 244 00:09:14,080 --> 00:09:16,920 那超时之后它直接错误返回 245 00:09:16,960 --> 00:09:19,680 好 然后在这儿信号量的等待队列呢 246 00:09:19,720 --> 00:09:22,480 我们是按先进先出排队 247 00:09:22,520 --> 00:09:24,040 这一条呢实际上 248 00:09:24,080 --> 00:09:27,040 在我们前面说到的锁机制里 249 00:09:27,080 --> 00:09:29,760 自旋锁它实际上是做不到的 250 00:09:29,800 --> 00:09:31,080 为什么呢 原因在于 251 00:09:31,120 --> 00:09:34,280 我们自旋锁是需要占用CPU 252 00:09:34,320 --> 00:09:36,120 随时随地去查 253 00:09:36,160 --> 00:09:38,160 有可能临界区的使用者 254 00:09:38,200 --> 00:09:40,400 退出的时候它刚改完 255 00:09:40,440 --> 00:09:44,120 下一个进入者是谁去查 256 00:09:44,160 --> 00:09:45,440 那它就能进去 257 00:09:45,480 --> 00:09:46,920 如果说你运气不好 258 00:09:46,960 --> 00:09:49,440 正好是这个资源变成有效 259 00:09:49,480 --> 00:09:50,960 你去查的时候在你之前 260 00:09:51,000 --> 00:09:52,440 就有一个人已经查过了 261 00:09:52,480 --> 00:09:53,520 好 那么这时候呢 262 00:09:53,560 --> 00:09:55,560 你就没有办法按照你 263 00:09:55,600 --> 00:09:57,800 等待的这个顺序来做这一条 264 00:09:57,840 --> 00:10:00,640 265 00:10:00,680 --> 00:10:03,240 好 那我们看信号量的实现 266 00:10:03,280 --> 00:10:05,360 我们把它定义成一个数据类型 267 00:10:05,400 --> 00:10:09,520 好那这里头呢有一个变量 sem(semaphore) 268 00:10:09,560 --> 00:10:12,080 然后有一个等待队列q 269 00:10:12,120 --> 00:10:14,560 那这些呢都是在操作系统内核里头的 270 00:10:14,600 --> 00:10:16,120 然后它定义两个操作 271 00:10:16,160 --> 00:10:17,840 P操作和V操作 272 00:10:17,880 --> 00:10:19,920 那在这里干些啥呢 273 00:10:19,960 --> 00:10:25,760 进来之后它是信号量的变量减1 274 00:10:25,800 --> 00:10:30,800 然后 如果你减完之后的值是小于0 275 00:10:30,840 --> 00:10:33,160 那么这时候呢就说明我没有资源了 276 00:10:33,200 --> 00:10:34,960 减到零是可以的 277 00:10:35,000 --> 00:10:36,560 好 那它把这个线程呢 278 00:10:36,600 --> 00:10:38,240 放到等待队列里头 279 00:10:38,280 --> 00:10:40,360 并且呢阻塞 280 00:10:40,400 --> 00:10:43,320 好 那这时候呢切换 281 00:10:43,360 --> 00:10:45,560 好 等到我申请到了 282 00:10:45,600 --> 00:10:47,200 那我就没有进入这个堵塞 283 00:10:47,240 --> 00:10:49,800 我就直接进到临界区里去执行 284 00:10:49,840 --> 00:10:51,080 那执行的过程当中呢 285 00:10:51,120 --> 00:10:54,080 用完了退出的时候作为啥呢 286 00:10:54,120 --> 00:10:57,120 信号量当中的变量加1 287 00:10:57,160 --> 00:11:01,040 好 如果说加一之后还小于0 288 00:11:01,080 --> 00:11:04,000 那就说实际上前面有人在等着 289 00:11:04,040 --> 00:11:05,240 好 那这时候呢 290 00:11:05,280 --> 00:11:08,960 我就从对应的等待队列里头 291 00:11:09,000 --> 00:11:12,920 把相应的线程放到就绪队列里头 292 00:11:12,960 --> 00:11:14,400 这和我们前面讲的 293 00:11:14,440 --> 00:11:16,680 进程的状态的变迁 294 00:11:16,720 --> 00:11:18,080 这样就结合起来了 295 00:11:18,120 --> 00:11:19,840 然后它结束 它就干别的去了 296 00:11:19,880 --> 00:11:22,040 那另一个 就可以进到临界区里头 297 00:11:22,080 --> 00:11:25,560 好 这是我们说到信号量的实现 298 00:11:25,600 --> 00:11:28,080 从这儿来讲好像这个东西很简单 299 00:11:28,120 --> 00:11:29,560 和我们前面区别在哪 300 00:11:29,600 --> 00:11:31,360 就是在于这一段代码 301 00:11:31,400 --> 00:11:33,680 执行的原子性 302 00:11:33,720 --> 00:11:35,800 它是由操作系统来保护的 303 00:11:35,840 --> 00:11:37,920 为啥我们前面做不到这样一点呢 304 00:11:37,960 --> 00:11:41,600 我这里头 减一和判断 305 00:11:41,640 --> 00:11:43,120 那我们在前面 306 00:11:43,160 --> 00:11:44,280 讨论软件方法的时候 307 00:11:44,320 --> 00:11:45,400 最大的问题就在于 308 00:11:45,440 --> 00:11:48,000 我减完一之后和这个判断这俩 309 00:11:48,040 --> 00:11:49,760 中间可能会中断 310 00:11:49,800 --> 00:11:51,040 好 由于这个中断 311 00:11:51,080 --> 00:11:52,600 我们就有无数的麻烦 312 00:11:52,640 --> 00:11:54,040 好 那这个时候现在有 313 00:11:54,080 --> 00:11:55,440 操作系统在里头做保护了 314 00:11:55,480 --> 00:11:56,680 好 我这两个代码 315 00:11:56,720 --> 00:11:59,200 它的执行不会被打断 316 00:11:59,240 --> 00:12:00,120 我们就会发现 317 00:12:00,160 --> 00:12:02,040 这个地方 我变成一个原子之后 318 00:12:02,080 --> 00:12:04,320 我的日子就好过多了 319 00:12:04,360 --> 00:12:06,200 好 那这是我们说 320 00:12:06,240 --> 00:12:08,640 信号量的基本原理 321 00:12:08,680 --> 00:12:09,920 下面我们会去 322 00:12:09,960 --> 00:12:11,000 用实际的例子 323 00:12:11,040 --> 00:12:12,160 来说明它的使用方法 324 00:12:12,200 --> 00:12:12,240