Python 并发的意义

Python 并发的意义,在只有单个处理器和一个计算核心的小型计算机中,处理器这个唯一的核心将所有计算串行化了。整个操作系统会通过巧妙的时间片分配交错出多个进程和多个线程的效果。

在多CPU或单CPU多计算核心的计算机上,可以对CPU指令进行一些实际的并发处理。其他所有类型的并发都是在操作系统层通过时间片模拟出来的。一台macOS X笔记本电脑可以有200个并发进程共享CPU,这里的进程数比可用的计算核心个数多得多。可见总体而言操作系统通过时间片负责绝大部分比较显见的系统并发行为。

边界条件

设想有一个算法,其算法复杂度为 O($n^2)。假设它有一个包含1000字节Python代码的内循环,那么在处理10 000个这样的对象时,需要执行1000亿次Python运算。我们称其为基本处理预算。只要我们觉得会有所帮助,那么就可以分配尽可能多的进程和线程,但处理预算并不会改变。

单个CPython字节码的执行时间不易统计。然而,在macOS X笔记本电脑上,长期平均值显示每秒可以执行60MB左右的代码。这意味着执行1000亿的字节码大约需要1666秒或28分钟。

如果有一台双处理器四核计算机,那么有可能将运行时间缩减到原来总时间的25%,即大约7分钟。这里我们假定将任务划分给了4个(或更多)独立的操作系统进程。

其中一个很重要的考量是1000亿字节码的总预算是无法改变的。并行性并不会神奇地减少工作负载,它只能通过改变对任务的调度来减少耗时。
如果切换到一个更好的、复杂度为 O(n\log_{n}) 的算法上,那么其工作负载可以从1000亿次运算减少到1.33亿次运算,运行时间大约是2秒。如果平均分配到4个核上,甚至在516毫秒内就能得到结果。并行性无法像改变算法这样显著提升性能。

进程或线程间共享资源

操作系统确保了进程之间很少或根本没有交互。在创建进程间必须交互的多进程应用时,必须显式地共享一个公共的操作系统资源。这可以是一个公共文件、一个共享内存的特定对象,或是进程间具有共享状态的一个信号量。进程本质上是彼此独立的,它们之间的交互只是一种特殊情况。

相反,多线程是单个进程的一部分,且一个进程中的所有线程共享操作系统资源。我们可以破例获取一些线程本地内存,这些内存可以在不干扰其他线程的情况下自由地写入。在线程本地内存之外,写内存操作会以不可预知的顺序设置进程的内部状态,因此必须通过显式加锁来避免。如前所述,指令执行的整个顺序严格来讲很少是并发的。并发线程和并发进程的指令通常会以不可预知的顺序在计算核心之间交错。线程化意味着可以对共享变量进行破坏性更新,并且需要通过加锁才能进行互斥访问。

裸机硬件层存在一些更复杂的写内存情况。关于写内存问题的更多信息,可参考维基百科词条memory disation。
在设计多线程应用程序时,并发对象更新的存在可能会造成混乱。加锁是避免并发写共享对象的一种方法。通常避免使用共享对象也是一种可行的设计技术。第二种技术——避免写共享对象更适用于函数式编程。
在CPython中,GIL用于确保操作系统的线程调度不会干扰Python内部数据结构的维护。实际上,GIL将调度的粒度从机器指令变成了Python虚拟机运算组。

GIL在确保数据结构完整性方面所产生的影响通常可以忽略不计,选用的算法对性能影响最大。

从何处受益

并发处理对执行大量计算和相对较少I/O操作的程序帮助不大。如果一个计算预计需要28分钟完成,那么以不同方式交错运算并不会有显著的影响。使用8个计算核心也许只能缩减大约1/8的时间。实际节省的时间取决于操作系统和语言本身的开销,而它们是很难预估的。引入并发对性能的影响不及使用更好的算法。
当计算涉及大量I/O时,通过交错CPU运算和I/O请求能显著提升性能。其核心思想是在等待操作系统完成其他数据I/O的同时计算一部分数据。由于I/O通常需要等待很长时间,因此一个八核处理器可以交错执行大量并发的I/O请求。
交叉计算和I/O的方法有两种,如下所示。

  • 可以创建一个阶段化处理的管道。每项数据必须经由读取、过滤、计算、聚合和写入等所有阶段。多并发阶段是指每个阶段处理不同的数据对象。阶段之间通过时间分片来实现计算和I/O的交错。
  • 可以创建一个并发工作池,每个worker负责对一个数据项执行所有计算。数据项分配给池中所有worker,由worker生成结果。

这些方法之间的区别不是特别明显,中间存在不是非此即彼的模糊区域。通常创建一个混合的结合体,用工作池来加速管道中的某个阶段,使其和管道中的其他阶段执行速度一样快。这里可以参照一些形式体系在一定程度上简化并发程序的设计。通信顺序进程(communicating sequential processes,CSP)范式可以辅助设计消息传递的应用程序。可以使用类似于pycsp这样的包向Python中添加CSP形式体系。

I/O密集型程序通常可以从并发处理中获得最大好处,其思想是交错I/O和计算。CPU密集型程序从并行处理中获得的好处相对较小。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程