并发(名词):指竞争或对抗。
并行:指两条直线永不相交的状态。
在并行和并发的问题上,我与Joe Armstrong(译注:Erlang语言发明者) 和 Rob Pike(译注:Go语言发明者)这俩人的看法并不一致。下面我以自动售货机和礼物盒为例来说明我的观点。(配有我用微软画笔精心制作的插图说明哦)
首先从概念上来说并行和并发都是非常流行的东东,很多编程语言和工具都会重点突出自己在这两个方面上的优异表现。
但我却认为并行和并发需要的是不同的工具,每个工具只会在某一个方面非常好,例如:
也有像Haskell这样的语言,对并行和并发的处理都很擅长处理。但是在Haskell内部这是属于两套不同的工具集,而且Haskell的官方wiki也解释了两者的使用原则,尽量避免在并行中使用并发处理:
最佳实践:优先使用并行,然后再考虑并发。
Haskell早就意识到一个工具不可能同时解决两个问题。专门针对并行环境设计的新兴编程语言Parsail,也面临同样的问题,虽然它也同时拥有针对并行和并发的工具集,但也建议避免在并行程序里使用并发特性,尤其在编写并发程序时要小心。
持完全相反观点的也有很多人,他们则认为能有效支持并发的工具或语言也会很好的支持并行的模式。Rob pike就说 Go语言具有很好的并发特性。这使得它适合进行并行编程。
利用好并发特性可以简化并行程序结构(提高可伸缩性等)。
同样,Joe Armstrong也说Erlang适合并行编程的原因是因为它具有并发特性,他还说现在还想用非并发的思想去解决并行的问题是 错误的思路。这与Rob pike的观点如出一辙。
到底是什么原因导致的这种分歧,为什么Haskell和Parsail的用户认为并发和并行是两种独立的模式,而go和Erlang的用户则认为并发和并行可以相辅相成呢?
我觉得是因为大家所要解决的问题不同,才导致了思路的不同,所以在看待并行和并发时所得出的结论就会不同。Joe Armstrong曾经画过一张图来解释这种区别。我也会画一些图来解释这件事情,首先来看下图,这张图和Joe Armstrong画的内容基本一样。
在实际情况下,的确会有单一队列的并发情况,但我还是按照Joe Armstrong的原始的图画了两个队列。从图中我们至少能得出以下结论?
给一群孩子分发礼物会怎么样呢?可乐罐和礼物之间有区别么?
事实上是有区别。自动售货机是一个事件处理系统:人们不定时的到来,并且从机器中取不定数量的罐。而礼物箱是一个计算系统:你知道孩子们的情况,你知道你买了什么礼物,并且你能决定谁能得到什么礼物以及如何得到。
在礼品盒的场景中,“并发与并行”看起来很不同:
那么从上面的例子中,我们可以看出并行和并发的如下区别:
在图7一大堆礼物的情况下,这个时候队列里并发含义是指一种竞争,对抗的状态:谁先到,谁就能拿到最好的乐高玩具。在俄语中,“concurrent(并发)”(读音:kon-koo-rent)和“competitor(竞争对手)”是同义词。
在图8里,每个礼物盒都写上了名字,每个名字和每个小朋友是一一对应的,他们是并行的,没有交叉,互相之间也没有冲突。(但为什么我在图中画上了箭头,使这些线相交了呢?一会我会解释这个问题。我们可以先这么想:每个孩子(类似系统中的进程)在搜索目标礼物(数据)的时候,会有相交,但这并不会造成冲突,也不会影响到谁最终拿到什么礼物)
运算和事件处理
电话客服、WEB服务、银行柜台等应用场景和自动售货机的应用是类似的,并发是他们面临的问题,必须解决不可预知的请求带来的不可避免的冲突,并行设计可以在一定程度上提高此类应用的处理速度,但其归根究底还是并发的问题。
而一些运算型的系统则类似写上名字的礼物,像图形处理,计算机视觉,科学计算等,在这样的系统中,并发不会成为问题,每个的输入和输出都是预先设计的,不会有意外的事件发生,并行的设计方法可以有效的解决此类问题,但也容易带来隐含的bug。
接下来将讨论运算系统和事件处理系统的差别所在,我们先从决定论的因果关系分析来入手,看看这里会有哪些微妙的差异。
确定性:可用和不可用的两种情况
在运算性系统中,结果是确定的,这可以使编程人员轻松很多----我们可以反复的去测试优化、甚至重构整个系统,然后通过结果就能知道我们做的对不对。
而且这种情况下,大家对确定性的要求往往不是那么严格----你并不在意最后找到哪100张图片,只要上面都有小猫就行,你也不在意最终会得到多么精确的PI值,只要知道它是在3和4之间就可以了。
但是在事件处理系统的模型下,什么都是不确定的,事件的不同顺序会产生不同的结果,如果我先到了,我会拿到最后的Coke罐,如果是银行的共享账户,我比你晚了一秒种,钱就会打给你。
当然,在实际的并发系统的开发测试过程中,你可以串行的运行程序,一次只处理一个事件。但一旦用真实的行为去并发的访问系统,你会发现结果和串行的时候是不一样的。
在测试环境中,你根据日志来重现银行共享账户的取钱行为,你运行两次就可能得到两个不同的结果。可能出现的结果的数量取决于系统中冲突的数量。
唉~~,还有比这更让人头疼的事吗?
如何判断并行安全:确定性和正确性
当程序以不同的顺序处理事件的时候,你怎么确定其中没有bug?
在运算系统中,只要你每次运行程序都能得到相同的结果,即使是错误的结果,你也可以基本断定程序是并行安全的。
就像运行一个搜索小猫的程序,但你每次都是搜出了小狗,那说明你的程序是有问题的,但是没有并行的安全问题。
但是在事件处理系统中,情况就复杂了一些,你唯一能确定系统是并行安全的依据,是每次系统都能得到正确的结果。
就像模拟两个人操作银行账户,现实情况下不可能每次的结果都是一样的,那如何判断这个程序没有bug呢?我们只能
从侧面来看,例如会不会导致账户的负余额,会不会连续两次取空一个账户,会不会凭空增加账户金额,等等。
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。 2KB翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。2KB项目(www.2kb.com,源码交易平台),提供担保交易、源码交易、虚拟商品、在家创业、在线创业、任务交易、网站设计、软件设计、网络兼职、站长交易、域名交易、链接买卖、网站交易、广告买卖、站长培训、建站美工等服务