Clean code 读书摘要
2017-03-06 本文已影响0人
简不闲
第13章 并发
“对象是过程的抽象。线程是调度的抽象。”——James O Coplien[1] 编写整洁的并发程序很难——非常难。编写在单线程中执行的代码简单得多。编写表面上看来不错、深入进去却支离破碎的多线程代码也简单。系统一旦遭受压力,这种代码就扛不住了。
看似并发13.1 为什么要并发
并发是一种解耦策略。它帮助我们把做什么(目的)和何时(时机)做分解开。在单线程应用中,目的与时机紧密耦合,很多时候只要查看堆栈追踪即可断定应用程序的状态。调试这种系统的程序员可以设定断点或者断点序列,通过查看到达哪个断点来了解系统状态。解耦目的与时机能明显地改进应用程序的吞吐量和结构。从结构的角度来看,应用程序看起来更像是许多台协同工作的计算机,而不是一个大循环。系统因此会更易于被理解,给出了许多切分关注面的有力手段。例如,Web应用的Servlet标准模式。这类系统运行于Web或EJB容器的保护伞之下,Web或EJB为你部分地处理并发问题。当有Web请求时,servlet就会异步执行。Servlet程序员无需管理所有的请求。原则上,每次 servlet是在自己的小世界中执行,与其他 servlet的执行是分离的。
当然,如果只是那么简单,也就没必要写这一章了。实际上,Web容器提供的解耦手段离完美还差得远。Servlet程序员得非常警惕、非常小心地保证并发程序不出错。同样,servlet模式的结构性好处还是很明显。但结构并非采用并发的唯一动机。有些系统对响应时间和吞吐量有要求,需要手工编写并发解决方案。例如,考虑一个单线程信息聚合程序,它从许多Web站点获取信息,再合并写入日志中。因为该系统是单线程的,它会逐个访问Web站点,在开始下一个之前等待当前站点访问完毕。每天的执行时间必须少于24个小时。然而,随着要访问的站点越来越多,采集所有数据花费的时间也越来越多,最终超过了24个小时的限制。单线程程序许多时间花在等待Web套接字I/O结束上面。通过采用同时访问多个站点的多线程算法,就能改进性能。或者,考虑某个每次花费1秒钟处理一个用户请求的系统。该系统在用户量较少的时候响应及时,但随着用户数增加,系统的响应时间也增加了。没人想排在150个人后面!通过并发处理多个用户请求,就能改进系统响应时间。再或者,考虑某个解释大量数据集、但只在处理完全部数据后给出一个完整解决方案的系统。或许可以在独立的计算机上处理每个数据集,那样的话许多数据集就能并行地得到处理。迷思与误解看来有足够的理由采用并发方案。然而,如前文所述,并发编程很难。如果你不那么细心,就会搞出不堪入目的东西来。看看以下常见的迷思和误解:(1)并发总能改进性能并发有时能改进性能,但只在多个线程或处理器之间能分享大量等待时间的时候管用。事情没那么简单。(2)编写并发程序无需修改设计事实上,并发算法的设计有可能与单线程系统的设计极不相同。目的与时机的解耦往往对系统结构产生巨大影响。(3)在采用Web或EJB容器的时候,理解并发问题并不重要实际上,你最好了解容器在做什么,了解如何对付本章后文将提到的并发更新、死锁等问题。下面是一些有关编写并发软件的中肯说法:并发会在性能和编写额外代码上增加一些开销;正确的并发是复杂的,即便对于简单的问题也是如此;并发缺陷并非总能重现,所以常被看做偶发事件[2]而忽略,未被当做真的缺陷看待;并发常常需要对设计策略的根本性修改。