在最简单最基础的层面上说,并行是指两个或更多的动作在同一时间发生,我们遇到的并行是生活中自然存在的,我们可以边说话边走或者每只手都进行不同的动作,当然我们每个人都是我们的生活中彼此独立的,你可以去看足球比赛当我游泳的时候,等等。
1.1.1 计算机中的并行
当我们讨论计算机的并行时,我们指一个系统执行多个独立的任务,而不是按顺序,或者一个接一个的,它不是一个新的现象:多任务操作系统允许一个计算机同时运行多个程序尽管任务切换已经初见很多年了。但是高端服务器的多进程的真实并行被使用更久。真实的运行多任务比只给人多任务的感觉将会更加流行。
历史上,很多计算机只有一个进程,但进程的核心在今天的许多桌面机器中依然存在,这样的机器在同一时间只能真正的执行一个任务,但是它能每秒在任务间切换很多次。通过执行一点任务的然后执行一点另一个任务,看上去任务像同时发生,这被称作任务切换,我们仍然讨论关于这样的系统,因为任务切换很快,你不能确定任务在哪一点上被执行和挂起,任务切换在两个用户和程序自身提供了一个虚拟的并行。因为只有一个虚拟的并行,运行在一个单进程的任务切换环境中的程序可能比真正的并行环境下的程序更巧妙。特别的内存模型的错误可能不会出现在这样的环境中,我们将在第10章讨论它。
计算机包含多进程已经被用在服务器和高端个人计算机任务很多年了,但是现在计算机在一个芯片上有多个核心已经在桌面机器中很流行,他们有多进程或者多核心在一个进程上,这些计算机就能支持真正多任务。我们称为硬件并行。
这里展示了一个理想化的情景,计算机正好执行两个任务。每个分10个相等的模块,在一个双核心机器上,每个任务可以运行在自己的核心上,在一个单核心的机器做任务切换,每个任务的模块间有间隙。为了执行交叉运行,系统在任务切换时必须进行上下文切换,这需要时间,为了运行上下文切换,系统必须保存CPU状态和运行任务的信息,进行切换,重新加载CPU状态为要运行的任务,CPU可能需要重新加载内存和缓存数据。这会造成延迟。
尽管硬件的并行在多喝喝多进程系统中是可用的,一些进程可以运行多线程在一个单核心,最重要的是一个真正的硬件线程数量,多少独立的任务可以被并行运行。尽管一个系统有真正的硬件并行。它很容易的有更多任务比硬件并行,所以任务切换仍要使用。例如,在一个特殊的桌面计算机中有成百上千的任务运行,执行后台操作,尽管这台计算机名义上是空闲的。任务切换允许这些后台程序运行,允许你文字处理,编译,编辑和浏览网站同时践行,1.2展示4个任务在一个双核机器上切换。有一个理想化的场景任务分割为几个模块,实际中有很多的问题导致分割不相等和不规则调度。这些问题将在第8章我们影响效率和性能的并行代码中讨论。
无论你的程序运行在一个单核心的机器或者一个多核心的机器上,本书覆盖的所有技术,函数和类可以被使用,并且无论并行被任务切换实现还是真正的并行。但是随着你的联想,怎样使用并行在你的程序中可能依赖硬件并行,将在第8章讨论。
1.1.2 并发实现方法
想象两个程序员同时工作在一个软件工程上,如果你的开发者在两个办公室,他们可能和平的进行他们的工作,互相没有交流,他们有自己的手册,然而,交流不是很简单,比起他们转身就能说话,他们必须使用电话或者email或者走到另一个的办公室,你必须有两个办公室和多个手册副本。
现在想象你移动你的开发者 到一个办公室,他们可以互相讨论,并且他们很容易分享他们的想法,有现在只有一个办公室,和一个手册,相反,他们可能发现他们很难全神贯注,并且他们可能要分享资源。
有两个方法组织你的开发者实现并行,每个开发者代表一个县城,每个办公室代表一个进程,第一个实现方法是有多个单线程的进程,它类似于每个开发者有自己的办公室,第二个是多个线程在一个进程,类似于两个开发者在一个办公室中。
你可以任意组合样式,多进程,多进程和单线程,但是原则是一样的,让我们看看两个并行在程序中的实现。
多进程并行:
第一个方法,在一个程序中使用并行去分割到多个分割的单线程进程中去运行。就像你可以同时运行你的浏览器和文字处理程序,分离的进程可以通过进程间通讯技术(信号,socket,文件)传输消息,负面是这样的通讯是复杂或者缓慢的,因为操作系统在进程间提供许多的保护避免一个进程修改另一个进程的数据,另外在进程间有一个内在的开销:花费时间去开启一个进程,操作系统必须分配内在资源去管理这个进程。
当然,它不是所有的都是负面的: 操作系统添加的进程间保护机制和高层的通讯机制意味着它可以更容易的写更安全的同步功能,的确,为Erlang程序语言提供的环境使用进程作为基本的并行编译模块实现更好的效果。
使用分离的进程实现并行也有一个额外的好处,你可以运行不同的进程通过网络连接在不同的机子上运行,尽管这回增加通讯开销,但它可以提高效率和性能。
多线程并行:
另一个方法是通过多线程在一个进程中实现并行。线程像轻量级的进程:每个线程互相独立,但是每个线程可能运行不同的指令序列,但是所有的线程在进程中分享地址空间,大多数的数据可以被所有的线程直接访问,全局变量,指针或者引用可以被传输在县城建。因此,它经常通过进程分享内存,这中通讯是非常难管理的,因为相同数据的内存地址不需要不同的进程。
共享地址空间和无保护的数据在线程间的开销可能比多进程小,因为操作系统不用登记,但是共享内存的灵活性也带来一些代价:如果数据被多线程访问,程序开发者必须保证数据是不变的,这个问题在这本书的3,4,5和8章会给出指导方针,这个问题是不可逾越的,写代码时要给出适当的关注,这意味着花费大量的精力考虑线程间通讯
多线程开销比多进程低意味着多线程是有利的并行在主流语言中,尽管线程间共享内存存在一些问题,但是C++标准不提供任何技术支持进程间通讯,所以使用多进程的程序必须依靠指定平台的API去做,这本书的重点将使用多线程实现并行,将来涉及到并行默认使用多线程。
已经简单的了解了什么是并行,让我们看一下问什么使用并行。