并发的错觉
今天聊一聊电脑和人脑的并发问题。
在计算机发展初期,CPU的计算能力非常有限,计算资源稀缺而昂贵。最早的时候一个CPU只能同时运行一个任务,这简直让人无法忍受。什么叫做只能运行一个程序呢?这就像大学上自习占座一样,一旦一本书、一张纸、一个包或一个活人占有了那个桌子,其他人就再也没法用了,无论是这个人出去上厕所,踢球,你都不能去用那个座位,如果你贼胆包天敢偷着去坐,这时候就会有个神秘人突然拍拍你的肩膀告诉你「童靴,这里有人」,这就是常说的「见鬼的故事」。故事里的座位就是CPU,无论当前任务在使用CPU进行计算,还是在读写磁盘IO或进行网络交互,丫都得占着CPU,黑客极客和各种无证程序员们觉得,这,不,科,学!
于是大家试图通过各种方式来改变这一现象。首先出场的是多通道程序,程序员们很快写了一个监控程序,发现当前任务不用CPU计算时,就唤醒其他等待CPU资源的程序,让CPU资源能够得到充分利用。但多通道的问题是调度乏力,不分青红皂白和轻重缓急,不管是急诊还是普通门诊,该等都得等。
第二出场的是分时系统,分时系统是一种协作模式,每个程序运行一小段时间都得主动把CPU让出来给其他程序,这样每个程序都有机会用到CPU一小段时间。这时操作系统的监控程序也完善了一些,能够处理相对复杂的请求。早期的Windows和Mac OS(注意没有X)都是采用这种方式来调度程序的。分时系统的问题是,一旦某个程序死循环,系统就没招了,只能干等着,就像死机了一模一样,程序员们说,这是不可接受的!
第三个隆重登场的是多任务系统,程序员们让操作系统接管了所有的硬件资源,变得更加高级智能,系统进程开始分级,有的是特权级别,有的是平民级别(你就知道,在计算机世界都特么是这个样子!),所有的应用程序以进程和线程方式运行,CPU的分配方式采用了抢占式,就是说操作系统可以强制把CPU的资源分配给目前最需要的程序。程序员们成功了,几乎完美的控制了一切,并造成了很多任务都在同时运行的假象,如果用两个字来形容的话,那就是「和谐」!目前OS X、Unix、Linux、Windows都是采用这种方式进行任务管理的。
以上都是单核单CPU的情况,但无论线程间的切换多么快,这些都是并发,而不是并行。
好吧,中间插播一段并发和并行的区别。
并发的英文单词是Concurrency,并行是Parallelism。
如果一个系统支持两个或多个动作(Action)同时存在,那就是一个并发系统。
如果一个系统支持两个或多个动作同时执行,那就是一个并行系统。
也就是说,单个CPU永远无法同时执行两个或以上的任务,但是允许任务同时存在。
所以,只有多核或多个CPU才可能发生并行,如果单核单CPU只能发生并发行为。
如果有人以为单核单CPU的并发就是同时执行很多任务,那么这是个错觉。
不知道解释清楚了木有。插播完毕!
终于,多核CPU和分布式系统被干出来了,一台计算机可以拥有多颗CPU,每颗CPU可以有多核,同时,成千上万台的机器被连接在一起进行计算,大家一看都晕了,史称「云计算」。随着硬件的变化,软件技术同时开始革新,各种语言开始支持并行计算,比如Erlang/Scala的Actor&Message模型,Go语言的goroutine机制,Java的ForkJoinPool,Objective-C的Grand Central Dispatch技术,当然还有Hadoop等分布式框架。
总之,到了目前这个阶段,无论是并发,还是并行,计算机和CPU都算是解放了,它们不仅在单台机器上可以执行并行计算,在横向扩展上也变得随心所欲,各种云平台应运而生,公有云私有云混合云balabala,反正是比较晕……
人脑就比较惨了,在电脑突飞猛进的这几十年里,几乎没有任何进展,脑袋仍然只有一个,也没有裂变出多核……
上文书谈了电脑的并发和并行的事情,有读者反馈,像一个有趣的教科书!我特么最烦教科书,就因为教科书,哥写了几年程序才把这些事捋清楚,为了让你们不再重蹈覆辙,我容易吗我!
好,下面我们谈一下人脑的并发,先看一个读者反馈,你们感受一下:
看过一个关于人脑的理论,说不清是并发还是异步。有时候我们很努力的想一个问题,但却怎么也想不起来,于是我们放弃了。但是大脑并没有放弃,此时它会自动起一个gorouting,继续在大脑的各个角落去寻找这个记忆碎片,当找到时执行回调告诉你。而此时你可能正边洗澡边哼着小曲儿!
关于人脑的机制,其复杂程度超过CPU何止万倍,比如上古奇人周伯通郭靖小龙女的双手互搏到底是并发还是并行呢,Mac君万不敢断言,未来还是让研究人脑图谱的人去探索其真正的奥秘吧。我们在这篇文章中把「一心二用」或「一心多用」统称为人脑的并发。
俗话说「一心不能二用」,这句话常常送给那些做事三心二意的人,但是我们真的不能一心多用吗?或者说并发带给我们的到底是效率的提升还是状态的下降?关于这件事Mac君的看法是,不可一概而论。「好吧,那位同学请把砖头继续放入怀中,我们还没有讲完」。
关于人脑的多任务处理,应该从个人特点、所处环境和任务特性来考虑。
其实人脑天生就是用来处理多任务的,比如你可以一边洗澡一边唱歌,一边看电影一边磕瓜子还要注意不要被飞来的砖头砸到等等,不过这样的多任务都是在放松环境下的简单任务,对我们提升效率没什么意义。
但是,当我们在健身房跑步时听英语,写文章或编码的时候听歌(所有不让听音乐编程的公司都将死于心碎),坐地铁的时候阅读,步行的时候思考,这就变得非常有意义的,因为我们在一个相对宽松的环境下把复杂的逻辑任务和简单的机械任务结合在一起,既不影响A,也不会干扰B,这种情况是我们优先要采取的并发策略。
类似的事情,比如开车时听英语,就要因人而异了。我有近10年的驾驶经验,喜欢开车,驾驶基本上已经形成下意识的动作,从出发到目的地往往不会记得自己做了哪些操作,所以我经常开车时听英语并有所收获。但有些人开车仅仅是驾驶已经够紧张忙乱了,倒一次车能车头入绝不车尾进,开次长途出的汗够洗澡的,那么就专心开车好了,车内最好保持安静或听一些舒缓的音乐。
我曾经看过一本叫做《错觉》的书,书中有一段描述了一位机长在飞机飞行的过程中发现机上设备出了点小故障,于是他和副机长一起排查,接着又找来机械师,哥三忙的不亦乐乎,过了一段时间,有人问,谁在开飞机呢?这时飞机无人驾驶已经很久了,等反应过来之后,飞机已经开始俯冲坠地,机上人员全部罹难!这种空难并不是意外,一架状况良好的飞机直接撞向地面不是偶尔发生,这种现象在航空领域被称作「可控飞行撞地」,其根本原因就是,人们太相信自己的多任务处理能力!
驾车虽然比驾驶飞机简单多了,但同样是一项非常危险的工作,所以我建议大家,听听音乐就好,另外千万别玩手机。
还有一种情况就是,在同一时间做两项或多项复杂任务,比如你让程序员在编码的同时帮助别人解决问题,能不能做好?也许有人可以,但我的感觉是,这种安排效率反而会打折扣。 人们在很多时候会低估自己的能力,但在更多时候会高估自己。在复杂任务并发处理的时候,人脑往往会高估自己的处理能力,以为可以,其实任务的并行,上下文的切换,注意力的分散,都会让你的效率大打折扣,所以设计模式中的职责单一原则不是盖的,一个类尽可能只做一件事情,无论是效率还是后期维护都会好很多,人脑其实也是一样。
总结一下:
- 简单任务的并发是大脑天生的nature,每个人都不在不自觉的应用。
- 在宽松的环境中让简单机械的任务和复杂有机的任务并行完成是非常不错的做法,提高效率节省时间。
- 在高危环境中(驾驶、高空作业等等)我们应该专心致志的只做当前的工作。
- 对于复杂任务,我们最好一件一件完成,即使有些人能够同时处理多重任务,那也需要长期的艰苦训练,比如郭靖君,你能否做到,就得看有没有周伯通那样的大哥!
后记:
今天给大家介绍一个命令来隐藏文件和文件夹,比如你不想在桌面上看到那么多文件和图标,或者不想让别人看到,只需要打开终端输入如下命令:
chflags hidden ~/Desktop/*
想恢复原状:
chflags nohidden ~/Desktop/*