|
深远影响
在过去30年里,并发虽然一直被鼓吹为“下一件大事”或“未来之路”,但软件界不为所动。现在,并行终于出现在我们面前了:新一代计算机全面支持并发,这将引发软件开发方式的巨变。
本文主要讨论并发对软件——包括编程语言和程序员——的深远影响。
Olukotun和Hammond所描述的硬件发展,代表着计算机计算方式的重大变化。过去30年里,半导体工业的发展及其在处理器上的应用,推动了已有顺序式软件运行速度的稳步提升。但体系结构上的多核处理器变化仅对并发应用有益,因此几乎对绝大多数现存软件没有价值。在不久的将来,已有的桌面应用不可能比现在跑得更快。实际上,它们的运行速度会稍慢,因为为了降低高密度多核处理器的电能消耗,新的芯片内核被简化并运行在更低的时钟速度。
这将对软件至少是主流软件的开发带来深远影响。计算机的能力无疑越来越强,但程序不再可能从硬件性能提升大餐中免费获益,除非它们实现了并发。
即便抛开多核变化的强制要求不谈,我们也有理由实现并发,尤其是将工作从同步转到异步,可以提高响应速度,就像目前的应用里必须让工作远离GUI线程,以便计算在后台进行时,屏幕能得到重绘。
但实现并发是有难度的。不仅目前的语言和工具仍未为将应用转化为并行程序做好充分准备,而且在主流应用中也很难找到并行,尤其糟糕的是——并发要求程序员以人类难以适应的方式思维。
不过,多核在未来不可回避,我们必须找到与之相适应的软件开发方式。接下来,我们将深入探讨并发的难度所在,以及一些未来可能的应对方向。
软件新纪元
今天的并发编程语言和工具几乎与结构化编程时代初期的顺序化编程同时起步。信号量和协程是实现并发的基本手段,锁与线程建立在更高层次,可以结构化构造并发程序。而我们需要的是以面向对象为基础的并发——建立在更高层次的抽象有助于构建并发程序,就像面向对象抽象有益于构建大型组件化程序一样。
几个因素决定了并发变革对我们冲击可能比面向对象大。首先,并发是获得更高性能的必需手段。像C之类的语言,无视面向对象,但仍能在很多开发领域发挥作用。如果并发变成了应用性能提升的华山一条路,那么商业和系统语言的存在价值就只能建立在支持并发编程的基础上。因此现存的如C之类的语言,就必须支持超越pthreads之流简单模式以外的并发特性,不能支持并发编程的语言将逐步走向消亡,而仅仅能在现代硬件不重要的场合占得一席之地。
并发比面向对象冲击更大的第二个原因是,尽管顺序化编程已经很难,但并发编程更难。例如,在分析顺序化程序时,环境相关性分析是考量环境影响行为的基础技术。并发程序则还需要进行同步分析,而同时做环境相关性和同步分析已经被证明不可企及
最后,人类在遭受并发的突然袭击后,发现并发程序比顺序化代码难以把握得多。即使最细心的程序员,也很可能考虑不到简单半有序作业集里的交叉问题。
客户端和服务端应用的差别
对客户端应用来说,并发是一个挑战。但是在很多服务端程序里,并发则是一个“已经解决的问题”,我们可以例行公事般地构造并发应用,结果它就能很好工作,尽管进一步改进程序以确保其更具扩展性,仍需艰巨努力。这些程序通常都包含大量并行,因为它们同时处理的是大量的彼此无关的请求。比如Web服务器和网站,运行的是同样代码的副本,处理绝大多数时候彼此无关的数据。
而且,它们的执行环境被隔离,通过高度支持并发存取结构化数据的数据库之类的抽象数据访问方式实现状态共享。因此,代码通过数据库实现数据共享,就能得到“安宁从容的感觉”——就好像运行在一个整洁、单线程的世界一样。
而客户端应用的世界里,则没有那么规则和结构化。通常,客户端程序为单用户执行相关的小型运算,由此出发,我们发现可以通过将运算分割为多个更有效率的小片断来实现并发。也就是说让用户界面和程序运算小片断可能以多种方式交互和共享数据。但这类程序难以并发执行,因为其代码是非均态的,结构密织、交互复杂,而且操作的是基于指针的数据结构。
并行
·编程模型(Programming Models)
现在,你能用多种方式实现并行,但每种方式仅仅适用于特定类型程序。大多数时候,没有细致入微的设计与分析,很难事先知道哪种模型适合给定问题。如果无法清楚确定给出的问题能套用哪种模型,往往就必须将多个模型杂合起来灵活运用。
这些并行编程模型可以从以下两个方面明确加以区分:并行作业的粒度,和任务间的耦合程度。这两个方面的不同,造就了迥异的编程模型。我们接下来依次讨论。
并行作业,小到单个指令,比如加法和乘法运算,大到需花费数小时甚至数天的程序。显然,并行体系中小作业的累计耗费是巨大的,因此,像并行指令运算等一般要求用硬件实现。与多处理器结构相比,多核处理器减少了通讯和同步消耗,因此减少了小片代码的累计成本。同样,一般来说,粒度划分越小,就越要注意拆分任务、以及为它们提供彼此间通讯和同步的成本。
另一方面是作业在通讯和同步时的耦合程度。不要有这样的幻想:作业完全独立执行,最后产生完全不同的输出。在这种情况下,各作业有序执行,不会引发同步和通讯问题,很容易实现无数据竞争可能的编程。这样的情况是罕见的,绝大多数程序的并发作业都要共享数据。因此作业更为变化多端,保证其正确和高效的复杂性也大为增加。最简单的情况是每个任务执行完全一样的代码。这种类型的共享常常是规则的,通过对单个任务的分析就能理解。更具挑战性的是不规则并行,这个时候,各作业的情况互不相同,共享模式更难于理解。
上一篇:免费午餐已经结束——软件向并发靠拢
下一篇:JAVA设计模式之事务处理
|