i++问题“阿Q,快回去,隔壁二车间胡子说我们改了他们的数据,来找麻烦了。”只好提前结束和小黑的通讯,赶回CPU一号车间。虎子见我回来,立马冲我吼道:“你这是怎么回事?几纳秒你就给我改了数据,你对这件事怎么看!”我听得发呆,他连连说:“虎子,你别着急,我刚回来,有什么事,先告诉我好不好?”接下来,老K跟我说了事情的经过。事实证明,负责我们两个CPU车间的每个线程都在执行i++操作。我们都把i的值放在自己的缓存中,完成后没有通知对方。我们添加了两次,但结果只有一次。数据不一致问题。原子操作明白原委后,我对虎子说:“大家都执行同样的代码,这不能怪我们。”虎子闻言急了:“怎么不怪你呢,我们抢在你前面了。”一步找到内存,把i拿走,然后就得等我们加完了。不信你打个电话问问记忆师,看看他是不是我们二车间先来的。”“好吧,你先冷静一下,你看,我们不知道你先去拿了,这不是天经地义的吗,现在出了事,我们还是坐下来,想办法避免这种事情发生吧。”以后不会再出问题了,你不觉得吗?”胡子叹了口气,问道:“那你说说,你能做什么?”我继续说:“看,像i++这样的操作,我们不应该被打扰”“不被打扰?”“是的,比如虎子,你二车间来i的时候,我们一车间是不能来的。我们需要等到您的访问结束,我们会再来。很简单的方法,但是非常有用。”虎子听完一愣,“这不就是锁定吗?你是不是要怪程序员在做i++之前不加锁?”“确实是加锁,但是这么简单的操作,加锁对程序员来说太麻烦了。我们CPU内部处理。“内部处理,你打算怎么实现?”虎子问道。“嗯,让我考虑一下……”虎子问具体的实现方式,我还没想到这一步。这时候,老K站了起来,说道:“我有办法,我可以找到总线主管,他是负责协调使用系统总线访问各个车间内存的指挥官。”他在中路协调应该不难。”K把做梦的人吵醒了,然后我们去找了busdirector,然后我们商量了一个解决办法:我们定义了一个东西,叫做atomicoperation,就是说这是一个不可分割的动作。执行原子操作,总线主控在系统总线上加一个LOCK#信号,其他车间要想访问内存,必须等到原子操作指令执行完毕。我们向领导汇报了这个计划,很快就得到了批准。以后我们八个车间就按照这个计划来工作。程序员把i++这样的动作换成原子操作后,问题就迎刃而解了。然而,实施一段时间后,各个车间开始抱怨:就因为某个车间需要进行原子操作,就让总线主管锁住系统总线,其他车间的人就不能访问内存,不能工作,严重影响工作效率。抱怨归抱怨,生活还得继续,直到没有更好的选择。缓存引起的问题然而没过多久,数据不一致的问题又出现了。这一次,不是加法的问题。我们两个车间由于各自的缓存,先后修改了变量的值。对方第一时间不知道,误用了错误的值,导致大错特错。“阿Q,上次解决的不错,这次解决不了问题。”虎子又找上门来了。“你来得正是时候,我正想和你谈谈这件事。”“哦,这样啊,难道你想到了解决办法?”“只是一些初步的想法,它们都有自己的私有缓存,修改数据后更新内存时互不打招呼,缺乏沟通机制。”虎子点头,“没错,所以我们需要建立一个通讯机制,更新各个工坊的缓存内容,统一管理吗?”“是啊!我们俩说说也没关系,我建议召集8个核心车间的代表开个会,详细讨论一下这个问题。哦,对了,公交车长也叫……”经验,他或许可以提供一些想法“缓存一致性协议MESI非常快,我们CPU的8个核心workshop就这个问题召开了会议,取得了非常重要的成果。我们采用了一条新的专线连接8个核心车间,进行车间之间的信息沟通。它不同于CPU外部的总线系统。大家把这个叫做on-chipbus。新线路铺好后,以后大家可以通过这条线路进行实时沟通。为了解决之前出现的问题,我们还制定了一套规则,叫做缓存一致性协议。规则规定所有车间的缓存单元——缓存行有四种状态:已修改(M):缓存行已被修改,与内存的值不同。如果其他CPU核心要读取内存中的数据,必须先将缓存行写回主存,并将状态变为共享(S)。Exclusive(E):缓存行只缓存在当前CPU核心中,与内存中的数据相同。当另一个CPU核心读取它时,状态变为共享状态;如果当前CPU内核修改了它,它就会变为已修改。SharedShared(S):缓存行存在于多个CPU核的缓存中,与内存中的内容一致。无效(I):缓存行无效。四种状态之间的转换是这样的:按照这套规则,大家不能再像以前那样随意了。每个车间在读写自己的缓存时,必须相互通信。喘口气,避免使用过时的数据。此外,还规定如果一个内存区域被多个作坊缓存,则不再允许多个作坊同时修改缓存。会议的另一个收获是,之前被workshops诟病的每一个原子操作都锁定了总线,同时解决了大家需要访问内存需要等待的问题。以后,总线主控不再需要锁定总线,可以通过这个缓存一致性协议来完成。自此,数据不一致的问题终于解决了,我们8个车间又可以愉快地工作了。
