0 00:00:00,000 --> 00:00:16,320 1 00:00:16,360 --> 00:00:20,720 今天我们来介绍同步互斥 2 00:00:20,760 --> 00:00:22,760 同步互斥是操作系统当中 3 00:00:22,800 --> 00:00:24,800 协调进程之间动作 4 00:00:24,840 --> 00:00:27,680 和相互关系的一种机制 5 00:00:27,720 --> 00:00:28,640 那在这里头呢 6 00:00:28,680 --> 00:00:31,400 我们通过生活当中的例子 7 00:00:31,440 --> 00:00:33,200 先来说明同步互斥 8 00:00:33,240 --> 00:00:35,200 到底是个什么样的问题 9 00:00:35,240 --> 00:00:38,280 我们有一些什么样的办法来解决它 10 00:00:38,320 --> 00:00:41,240 然后呢是在计算机系统当中 11 00:00:41,280 --> 00:00:44,840 我们给出三类不同的做法 12 00:00:44,880 --> 00:00:47,520 我们下面会一一来介绍它 13 00:00:47,560 --> 00:00:50,840 首先我们来介绍同步互斥的背景 14 00:00:50,880 --> 00:00:53,920 那就是说为什么我们要做这件事情 15 00:00:53,960 --> 00:00:56,840 那在以前我们在写程序的时候呢 16 00:00:56,880 --> 00:00:58,160 我写一个独立的程序 17 00:00:58,200 --> 00:00:59,480 这个程序呢它不需要 18 00:00:59,520 --> 00:01:03,760 和其它的进程共享状态和资源 19 00:01:03,800 --> 00:01:06,760 那在这种情况下它的确定性 20 00:01:06,800 --> 00:01:09,640 和可重现呢是可以保证的 21 00:01:09,680 --> 00:01:11,400 也就说你只要是 22 00:01:11,440 --> 00:01:13,680 输入的状态是一致的 23 00:01:13,720 --> 00:01:15,080 那么它输出的结果呢 24 00:01:15,120 --> 00:01:17,600 一定就是相同的 25 00:01:17,640 --> 00:01:19,400 然后我第一遍执行 26 00:01:19,440 --> 00:01:23,640 和第二遍执行它的结果是一样的 27 00:01:23,680 --> 00:01:26,400 但是由于我们在实际的程序当中呢 28 00:01:26,440 --> 00:01:28,640 如果说你在这里头 29 00:01:28,680 --> 00:01:30,160 以这种方式来做的话 30 00:01:30,200 --> 00:01:31,720 那我们这个程序的功效呢 31 00:01:31,760 --> 00:01:33,120 就很有限了 32 00:01:33,160 --> 00:01:33,800 通常情况下 33 00:01:33,840 --> 00:01:35,360 我们写好一个程序之后呢 34 00:01:35,400 --> 00:01:37,800 是希望它在每一次做的时候呢 35 00:01:37,840 --> 00:01:39,960 它对不同的数据进行处理 36 00:01:40,000 --> 00:01:41,800 那这时候我们 37 00:01:41,840 --> 00:01:44,080 在中间的过程当中 执行的时候 38 00:01:44,120 --> 00:01:47,520 这个顺序就不是很重要 39 00:01:47,560 --> 00:01:50,320 而我们在操作系统当中呢 40 00:01:50,360 --> 00:01:51,960 有了多进程之后 41 00:01:52,000 --> 00:01:53,280 这时候会有多个 42 00:01:53,320 --> 00:01:56,240 并发的进程交替执行 43 00:01:56,280 --> 00:01:57,480 这种交替执行呢 44 00:01:57,520 --> 00:02:01,000 会导致进程之间有资源共享 45 00:02:01,040 --> 00:02:03,560 比如说大家都要占用CPU 46 00:02:03,600 --> 00:02:04,920 那我是时分的 47 00:02:04,960 --> 00:02:06,800 大家都要占用内存 48 00:02:06,840 --> 00:02:08,880 那我是把一块区域分配给你 49 00:02:08,920 --> 00:02:11,440 把另一块区域分配给另外一个进程 50 00:02:11,480 --> 00:02:14,240 正是由于这种资源的共享 51 00:02:14,280 --> 00:02:15,840 这种不确定性和 52 00:02:15,880 --> 00:02:18,600 不可重现呢就会产生 53 00:02:18,640 --> 00:02:21,400 那如果说在这里头你的程序的结果 54 00:02:21,440 --> 00:02:23,640 对这种不确定性 或者说 55 00:02:23,680 --> 00:02:27,360 这种不一致性是没有依赖的 56 00:02:27,400 --> 00:02:29,560 那我们在这里变化这一部分呢 57 00:02:29,600 --> 00:02:30,840 不影响到你的结果 58 00:02:30,880 --> 00:02:31,920 那这时候我们程序 59 00:02:31,960 --> 00:02:33,880 应该也算是正确的 60 00:02:33,920 --> 00:02:35,440 但是一些时候呢 61 00:02:35,480 --> 00:02:37,680 这种状态的不一致呢 62 00:02:37,720 --> 00:02:40,280 会导致你运行结果的不一致 63 00:02:40,320 --> 00:02:42,640 甚至于有些情况下这种不一致呢 64 00:02:42,680 --> 00:02:44,800 就是我们希望它出现的 65 00:02:44,840 --> 00:02:47,360 比如说我两个进程之间在通讯 66 00:02:47,400 --> 00:02:49,880 那根据通讯对方回过来信息的不同 67 00:02:49,920 --> 00:02:51,200 我这边处理是不一样的 68 00:02:51,240 --> 00:02:53,600 而这种在通讯的过程当中两者之间 69 00:02:53,640 --> 00:02:58,640 保证它可重现的这事是不行的 70 00:02:58,680 --> 00:02:59,840 那在这种情况下 71 00:02:59,880 --> 00:03:02,280 我们要想保证一个进程执行的 72 00:03:02,320 --> 00:03:04,520 正确性的难度就增加了 73 00:03:04,560 --> 00:03:05,440 好 这时候会出现一类 74 00:03:05,480 --> 00:03:06,400 什么样的错误呢 75 00:03:06,440 --> 00:03:10,760 就是我们有一些错误它是间歇性的 76 00:03:10,800 --> 00:03:12,440 也就说你第一遍执行 77 00:03:12,480 --> 00:03:14,360 那外界的环境跟现在不一样 78 00:03:14,400 --> 00:03:15,520 那也许没错 79 00:03:15,560 --> 00:03:17,080 到另一次执行的时候呢 80 00:03:17,120 --> 00:03:18,480 它可能就出错了 81 00:03:18,520 --> 00:03:19,440 实际上这是我们在 82 00:03:19,480 --> 00:03:20,600 实际写程序当中呢 83 00:03:20,640 --> 00:03:21,880 经常碰这种情况 84 00:03:21,920 --> 00:03:23,280 你自己在那儿测试的时候 85 00:03:23,320 --> 00:03:24,560 你的程序都一切正常 86 00:03:24,600 --> 00:03:27,320 交给用户用的时候情况就变了 87 00:03:27,360 --> 00:03:28,840 那你说我在这里严格测试之后 88 00:03:28,880 --> 00:03:29,920 为啥会不一样呢 89 00:03:29,960 --> 00:03:31,680 实际上这里头你所说的严格 90 00:03:31,720 --> 00:03:33,640 总会有一些依赖的环境 91 00:03:33,680 --> 00:03:35,680 跟你在测试的时候 92 00:03:35,720 --> 00:03:38,320 和真实系统运行的时候 是不一样的 93 00:03:38,360 --> 00:03:40,000 好 那你说这样一来的话 94 00:03:40,040 --> 00:03:41,720 这件事情带来这么多麻烦 95 00:03:41,760 --> 00:03:43,520 我们不这样做不就行了吗 96 00:03:43,560 --> 00:03:45,600 但是进程的并发执行呢 97 00:03:45,640 --> 00:03:47,360 又给我们带来很多的好处 98 00:03:47,400 --> 00:03:48,200 这些好处呢 99 00:03:48,240 --> 00:03:50,520 导致于我们非常希望这样做 100 00:03:50,560 --> 00:03:52,280 也就是说进程要与 101 00:03:52,320 --> 00:03:54,480 计算机系统当中的其它进程 102 00:03:54,520 --> 00:03:57,040 或者是设备要进行协作 103 00:03:57,080 --> 00:03:57,960 那这种协作呢 104 00:03:58,000 --> 00:03:59,720 可以带来这样一些好处 105 00:03:59,760 --> 00:04:02,480 第一个是说共享资源 106 00:04:02,520 --> 00:04:04,400 你比如说有多个用户 107 00:04:04,440 --> 00:04:06,320 想使用一台机器 108 00:04:06,360 --> 00:04:07,640 那他们之间呢 109 00:04:07,680 --> 00:04:08,840 这台计算机资源呢 110 00:04:08,880 --> 00:04:10,160 就是大家共享的 111 00:04:10,200 --> 00:04:10,800 好 那这样的话 112 00:04:10,840 --> 00:04:12,080 我们可以节约成本 113 00:04:12,120 --> 00:04:14,680 然后说你的钱存到银行里头 114 00:04:14,720 --> 00:04:16,880 但你并不一定什么时间在什么地方 115 00:04:16,920 --> 00:04:18,800 那这个银行帐号的余额呢 116 00:04:18,840 --> 00:04:20,800 我可以在多台取款机上操作 117 00:04:20,840 --> 00:04:21,480 那这时候我就可以 118 00:04:21,520 --> 00:04:23,960 就近存取我的现金 119 00:04:24,000 --> 00:04:26,840 那这会是给你带来好处的 120 00:04:26,880 --> 00:04:30,000 然后说我们在嵌入式系统里头 121 00:04:30,040 --> 00:04:31,520 你比如说机器人 122 00:04:31,560 --> 00:04:34,440 那上头呢它有多种设备 123 00:04:34,480 --> 00:04:36,400 比如说 我们的手臂和手 124 00:04:36,440 --> 00:04:38,280 之间的控制机构 125 00:04:38,320 --> 00:04:39,400 它们之间要协调 126 00:04:39,440 --> 00:04:40,480 因为只有在这种情况 127 00:04:40,520 --> 00:04:41,560 你协调到一起 128 00:04:41,600 --> 00:04:44,120 你才能够完成一个复杂的动作 129 00:04:44,160 --> 00:04:45,160 比如说机器人的 130 00:04:45,200 --> 00:04:47,480 直立行走或者说跑步 131 00:04:47,520 --> 00:04:50,120 那这些呢都需要多个机构来协调的 132 00:04:50,160 --> 00:04:51,400 这些共享呢 133 00:04:51,440 --> 00:04:53,240 是我们这里必须要做的 134 00:04:53,280 --> 00:04:54,640 然后第二个好处呢 135 00:04:54,680 --> 00:04:57,760 它是可以提高速度 136 00:04:57,800 --> 00:04:58,880 比如说我们在这里头 137 00:04:58,920 --> 00:05:02,400 我们在前面讲进程的时候说 138 00:05:02,440 --> 00:05:03,920 CPU是管计算 139 00:05:03,960 --> 00:05:06,560 设备是跟外界做交互的 140 00:05:06,600 --> 00:05:08,000 这两个实际上它们是可以 141 00:05:08,040 --> 00:05:11,320 在时间重叠上可以一起工作的 142 00:05:11,360 --> 00:05:13,040 那它是并行 143 00:05:13,080 --> 00:05:14,680 如果说你在算的时候 144 00:05:14,720 --> 00:05:16,320 I/O设备你不能让它工作 145 00:05:16,360 --> 00:05:18,840 那这时候呢I/O设备就只能闲着 146 00:05:18,880 --> 00:05:20,160 而你在做I/O的时候 147 00:05:20,200 --> 00:05:21,480 CPU没有相应的数据 148 00:05:21,520 --> 00:05:22,560 它也没办法做 149 00:05:22,600 --> 00:05:23,320 好这样的话 150 00:05:23,360 --> 00:05:25,160 这两个效率就降低了 151 00:05:25,200 --> 00:05:26,200 那我们希望呢 152 00:05:26,240 --> 00:05:28,320 这两个能很合理进行安排 153 00:05:28,360 --> 00:05:30,680 以便于它们俩可以同时工作 154 00:05:30,720 --> 00:05:31,880 那这样的话 155 00:05:31,920 --> 00:05:34,520 程序运行的速度就提高了 156 00:05:34,560 --> 00:05:35,680 再有一个呢是说 157 00:05:35,720 --> 00:05:37,800 我如果说系统里有多个处理机 158 00:05:37,840 --> 00:05:40,880 我把一个程序切成若干个模块 159 00:05:40,920 --> 00:05:44,040 这些模块呢可以作为独立的程序 160 00:05:44,080 --> 00:05:46,720 跑到不同的处理机上 161 00:05:46,760 --> 00:05:48,480 那这时候它可以并行执行 162 00:05:48,520 --> 00:05:50,560 也可以提高它的速度 163 00:05:50,600 --> 00:05:52,760 这是提高速度两种做法 164 00:05:52,800 --> 00:05:54,080 然后还有一种呢 165 00:05:54,120 --> 00:05:55,960 我们可以把程序模块化 166 00:05:56,000 --> 00:05:58,280 我用多个进程来 167 00:05:58,320 --> 00:06:00,080 完成一个综合的功能 168 00:06:00,120 --> 00:06:02,440 比如说我们在这里头说到编译 169 00:06:02,480 --> 00:06:04,360 以编译的过程为例 170 00:06:04,400 --> 00:06:06,080 编译呢 我们可以把它分成 171 00:06:06,120 --> 00:06:07,760 源代码模块的编译 172 00:06:07,800 --> 00:06:10,640 然后库的编译 和最后的链接 173 00:06:10,680 --> 00:06:11,720 这样几个部分 174 00:06:11,760 --> 00:06:12,680 那这几个部分呢 175 00:06:12,720 --> 00:06:14,760 我们把它写成独立的程序 176 00:06:14,800 --> 00:06:16,360 那这样的话它们组合到一起 177 00:06:16,400 --> 00:06:19,480 进行复用和扩展呢也会比原来方便 178 00:06:19,520 --> 00:06:20,800 所以基于这样的理由呢 179 00:06:20,840 --> 00:06:25,080 我们需要让多个进程并发执行 180 00:06:25,120 --> 00:06:26,280 好 那我们刚才说 181 00:06:26,320 --> 00:06:29,080 并发执行有状态的变化 182 00:06:29,120 --> 00:06:31,040 这些状态变化可能影响到 183 00:06:31,080 --> 00:06:33,200 我程序执行的结果 184 00:06:33,240 --> 00:06:34,680 那在这儿呢我们就举一个例子 185 00:06:34,720 --> 00:06:37,080 看看到底它会有什么样的影响 186 00:06:37,120 --> 00:06:39,960 这些影响我们怎么来控制它 187 00:06:40,000 --> 00:06:41,360 在这儿举的例子是说 188 00:06:41,400 --> 00:06:43,080 我们在操作系统里 189 00:06:43,120 --> 00:06:46,200 有一个进程创建的系统调用fork 190 00:06:46,240 --> 00:06:49,480 这个创建进程的系统调用呢 191 00:06:49,520 --> 00:06:51,440 它要给每一个新创建进程呢 192 00:06:51,480 --> 00:06:54,760 分配一个进程ID 一个标识 193 00:06:54,800 --> 00:06:56,600 那这个标识的分配呢 194 00:06:56,640 --> 00:06:57,960 在我们系统里头呢 195 00:06:58,000 --> 00:07:00,280 就会是对应过来这样一行代码 196 00:07:00,320 --> 00:07:02,160 在操作系统内核里头 197 00:07:02,200 --> 00:07:03,560 fork的实现代码里头 198 00:07:03,600 --> 00:07:05,120 也能找到这样一行 199 00:07:05,160 --> 00:07:07,040 说我一个进程的ID 200 00:07:07,080 --> 00:07:09,440 那是系统里呢有一个全局变量 201 00:07:09,480 --> 00:07:11,800 这个变量呢叫nextpid 202 00:07:11,840 --> 00:07:14,320 好 我把它赋值并且加1 203 00:07:14,360 --> 00:07:16,720 也就是说把当前的值 204 00:07:16,760 --> 00:07:18,520 赋给你新分配的这个进程 205 00:07:18,560 --> 00:07:20,360 然后把它的值呢加1 206 00:07:20,400 --> 00:07:22,280 这样的话下一次再来分配的时候 207 00:07:22,320 --> 00:07:25,240 我就是接下来这个值了 208 00:07:25,280 --> 00:07:26,560 那这个代码呢 209 00:07:26,600 --> 00:07:28,240 我们在实现的时候呢 210 00:07:28,280 --> 00:07:29,880 机器在编译的时候 211 00:07:29,920 --> 00:07:32,960 就会把它转换成这样几条汇编指令 212 00:07:33,000 --> 00:07:35,160 在源代码里就一行 213 00:07:35,200 --> 00:07:37,480 那这翻译成的这四条指令 214 00:07:37,520 --> 00:07:40,040 你拿过来执行呢应该也是没问题的 215 00:07:40,080 --> 00:07:43,200 它是把你这个全局变量里的ID 216 00:07:43,240 --> 00:07:45,440 读到一个寄存器里头 217 00:07:45,480 --> 00:07:47,600 然后再把这个寄存器 218 00:07:47,640 --> 00:07:49,320 赋值到你这个进程 219 00:07:49,360 --> 00:07:52,120 自己的进程控制块里的变量 220 00:07:52,160 --> 00:07:53,440 newpid 221 00:07:53,480 --> 00:07:55,920 然后把它这个寄存器值加1 222 00:07:55,960 --> 00:07:57,640 然后再把加1之后的结果呢 223 00:07:57,680 --> 00:08:00,480 重回到你原来的nextpid 224 00:08:00,520 --> 00:08:01,800 好 这个过程就结束了 225 00:08:01,840 --> 00:08:02,960 那正常情况下执行呢 226 00:08:03,000 --> 00:08:04,320 这也是没有问题的 227 00:08:04,360 --> 00:08:05,200 好 我们希望它 228 00:08:05,240 --> 00:08:07,400 预期的结果呢是这样的 229 00:08:07,440 --> 00:08:10,640 假定我开始的时候对pid是100 230 00:08:10,680 --> 00:08:13,000 一个进程调用fork 231 00:08:13,040 --> 00:08:15,840 它由100变成101 232 00:08:15,880 --> 00:08:18,920 然后第二个进程再调用fork 233 00:08:18,960 --> 00:08:20,600 那它分配到101 234 00:08:20,640 --> 00:08:23,600 然后把当前值呢变成102 235 00:08:23,640 --> 00:08:25,720 好 那这是我们期望的结果 236 00:08:25,760 --> 00:08:29,560 好 那在这个过程当中我们看一下 237 00:08:29,600 --> 00:08:32,120 如果说我的执行顺序 238 00:08:32,160 --> 00:08:34,800 在一种特定的顺序下 239 00:08:34,840 --> 00:08:37,840 这个预期的结果能出现吗 240 00:08:37,880 --> 00:08:38,880 这就是我们这里说到的 241 00:08:38,920 --> 00:08:40,960 一种可能的错误 242 00:08:41,000 --> 00:08:44,680 好 假定说我有两个进程 A和B 243 00:08:44,720 --> 00:08:46,080 那它在运行的时候呢 244 00:08:46,120 --> 00:08:47,320 调用fork之前呢 245 00:08:47,360 --> 00:08:48,600 它自己有自己的一个 246 00:08:48,640 --> 00:08:50,520 局部变量 new_pid 247 00:08:50,560 --> 00:08:52,080 这里面也有一个 248 00:08:52,120 --> 00:08:53,440 它俩的名字是一样的 249 00:08:53,480 --> 00:08:56,360 好 整个系统里呢有一个nextpid 250 00:08:56,400 --> 00:08:58,200 那 当前值它是100 251 00:08:58,240 --> 00:09:00,000 好 那我们这时候呢调用fork 252 00:09:00,040 --> 00:09:02,160 我们重点关注是刚才那条 253 00:09:02,200 --> 00:09:06,160 分配进程标识的代码 254 00:09:06,200 --> 00:09:08,920 它所对应的汇编代码的执行顺序 255 00:09:08,960 --> 00:09:12,480 好 我们首先呢是这四条汇编语句 256 00:09:12,520 --> 00:09:14,080 那应该是说在中间 257 00:09:14,120 --> 00:09:17,240 它会可能出现切换 258 00:09:17,280 --> 00:09:21,240 先上来之后读nextpid 259 00:09:21,280 --> 00:09:25,000 然后这个时候呢发生了一次切换 260 00:09:25,040 --> 00:09:28,120 到进程B再去读这个pid 261 00:09:28,160 --> 00:09:29,960 好 那这两条执行完了之后 262 00:09:30,000 --> 00:09:35,720 我们看到你newpid就已经变成了100 263 00:09:35,760 --> 00:09:38,200 而进程B呢也变成了100 264 00:09:38,240 --> 00:09:39,960 那这时候实际上我们就 265 00:09:40,000 --> 00:09:41,400 已经发现它有问题了 266 00:09:41,440 --> 00:09:44,920 它俩是相同的值 267 00:09:44,960 --> 00:09:47,240 那你给两个不同新创建进程 268 00:09:47,280 --> 00:09:49,440 分配了一个相同标识 269 00:09:49,480 --> 00:09:51,160 那这时候会有麻烦的 270 00:09:51,200 --> 00:09:53,080 好 我们看 好 然后这时候呢 271 00:09:53,120 --> 00:09:57,320 再执行加1写回去 这地方变成101 272 00:09:57,360 --> 00:10:00,040 好 然后这回切换回来 273 00:10:00,080 --> 00:10:01,640 到右边的进程 274 00:10:01,680 --> 00:10:04,080 再加1赋值 275 00:10:04,120 --> 00:10:06,480 那这地方还是101 276 00:10:06,520 --> 00:10:08,720 这个赋值就被赋值了两遍 277 00:10:08,760 --> 00:10:10,960 并且是相同的值 278 00:10:11,000 --> 00:10:12,480 好 那么这时候大家看 279 00:10:12,520 --> 00:10:14,040 在这个过程当中 280 00:10:14,080 --> 00:10:16,800 出现了什么一情况 281 00:10:16,840 --> 00:10:19,560 两个进程的分配的ID是一样的 282 00:10:19,600 --> 00:10:20,960 并且新创建的变化完了之后 283 00:10:21,000 --> 00:10:23,120 ID它不是创建了两个进程 284 00:10:23,160 --> 00:10:25,920 我应该加2 这地方它只加了1 285 00:10:25,960 --> 00:10:27,600 好 那这时候出现 286 00:10:27,640 --> 00:10:29,640 这种情况的原因是什么 287 00:10:29,680 --> 00:10:30,720 原因就是在于 288 00:10:30,760 --> 00:10:34,680 我们正常假设这四条是一块执行的 289 00:10:34,720 --> 00:10:39,360 读出来 赋值 然后加1写回去 290 00:10:39,400 --> 00:10:43,560 这边呢读出来赋值做了 291 00:10:43,600 --> 00:10:47,080 加1写回去这件事情中间被切断了 292 00:10:47,120 --> 00:10:48,320 好 切断之后 293 00:10:48,360 --> 00:10:52,040 这两头的加1就变成是相同的了 294 00:10:52,080 --> 00:10:54,600 写回去也就变成相同的了 295 00:10:54,640 --> 00:10:55,640 好 那这个时候呢 296 00:10:55,680 --> 00:10:58,880 我们这个状态就出结果了 297 00:10:58,920 --> 00:11:00,560 好 这时候出现这种的问题的 298 00:11:00,600 --> 00:11:01,440 原因在什么地方 299 00:11:01,480 --> 00:11:04,720 就是读出加1这个操作 300 00:11:04,760 --> 00:11:08,760 它并没有是一个整体在进行操作 301 00:11:08,800 --> 00:11:11,920 这就是我们这里说的原子操作 302 00:11:11,960 --> 00:11:13,920 原子操作是指一次 303 00:11:13,960 --> 00:11:17,720 不存在任何中断或失败的操作 304 00:11:17,760 --> 00:11:20,240 如果出现了中间的失败 305 00:11:20,280 --> 00:11:22,760 中断那这时候是不行的 306 00:11:22,800 --> 00:11:24,840 在生活当中的原子操作是什么呢 307 00:11:24,880 --> 00:11:26,680 你比如说我到银行里去 308 00:11:26,720 --> 00:11:28,920 存款或者取款 309 00:11:28,960 --> 00:11:31,240 那你是有一个假设的 310 00:11:31,280 --> 00:11:33,640 说我把钱给到窗口里头 311 00:11:33,680 --> 00:11:35,840 那么这时候呢我的存折上的钱 312 00:11:35,880 --> 00:11:38,160 金额是要发生变化的 313 00:11:38,200 --> 00:11:39,720 这两个一起发生 314 00:11:39,760 --> 00:11:42,800 那储户没有任何意见 315 00:11:42,840 --> 00:11:45,400 好 如果这两个分开 316 00:11:45,440 --> 00:11:46,520 你存进去钱了 317 00:11:46,560 --> 00:11:48,840 但是你的存折上的 318 00:11:48,880 --> 00:11:50,480 金额没有发生变化 319 00:11:50,520 --> 00:11:51,280 要么你不同意 320 00:11:51,320 --> 00:11:52,800 要么银行不同意 321 00:11:52,840 --> 00:11:53,600 好 那这是我们 322 00:11:53,640 --> 00:11:55,240 这里说的原子操作 323 00:11:55,280 --> 00:11:57,000 那要求要么成功执行 324 00:11:57,040 --> 00:11:58,800 要么没有操作 325 00:11:58,840 --> 00:12:01,920 不会出现部分执行的状态 326 00:12:01,960 --> 00:12:04,320 这是我们在这里头呢 327 00:12:04,360 --> 00:12:05,400 如果说我们刚才 328 00:12:05,440 --> 00:12:08,560 pid加1的这个操作 329 00:12:08,600 --> 00:12:09,560 它是个原子 330 00:12:09,600 --> 00:12:10,960 那我们这儿就没问题了 331 00:12:11,000 --> 00:12:12,880 现在的问题是我们把它分成两段 332 00:12:12,920 --> 00:12:14,640 中间做了个切换 333 00:12:14,680 --> 00:12:16,440 这是我们要面临的挑战 334 00:12:16,480 --> 00:12:18,080 所以我们今天讨论 335 00:12:18,120 --> 00:12:20,040 同步互斥问题的时候 336 00:12:20,080 --> 00:12:21,960 就是要在操作系统里头 337 00:12:22,000 --> 00:12:24,520 提供一种同步机制 338 00:12:24,560 --> 00:12:26,840 既允许并发执行 339 00:12:26,880 --> 00:12:28,720 以便于我能做到资源共享 340 00:12:28,760 --> 00:12:31,080 和提高速度 341 00:12:31,120 --> 00:12:33,440 同时我要让一些操作呢 342 00:12:33,480 --> 00:12:35,000 是原子操作 343 00:12:35,040 --> 00:12:36,640 因为你不是原子操作之后 344 00:12:36,680 --> 00:12:39,280 所带来的麻烦就会很大 345 00:12:39,320 --> 00:12:41,560 那这是我们在这里的需求 346 00:12:41,600 --> 00:12:42,520 接下来我们会来说 347 00:12:42,560 --> 00:12:44,000 我们有一些什么样的办法 348 00:12:44,040 --> 00:12:45,360 来解决这个问题 349 00:12:45,400 --> 00:12:45,440