在计算机设计早期,为了响应更多计算性能的需要,单处理器系统发展成为多处理器系统。更现代的、类似的系统设计趋势是将多个计算核放到单个芯片。无论多个计算核是在多个CPU芯片上还是在单个 CPU 芯片上,我们称之为多核或多处理器系统。
多线程编程提供机制,以便更有效地使用这些多个计算核和改进的并发性。考虑一个应用,它有 4 个线程。对于单核系统,并发仅仅意味着线程随着时间推移交错执行(图 1),因为处理核只能同一时间执行单个线程。
不过,对于多核系统,并发表示线程能够并行运行,因为系统可以为每个核分配一个单独线程(图 2)。
注意这里讨论的并行性和并发性的区别。并行系统可以同时执行多个任务。相比之下,并发系统支持多个任务,允许所有任务都能取得进展。因此,没有并行,并发也是可能的。
在 SMP 和多核架构出现之前,大多数计算机系统只有单个处理器。CPU 调度器通过快速切换系统内的进程,以便允许每个进程取得进展,从而提供并行假象。这些进程并发运行,而非并行运行。
随着系统线程数量从几十个到几千上万个,CPU 设计人员通过增加硬件来改善线程性能的提高系统性能。现代 Intel CPU 的每个核经常支持两个线程,而 Oracle T4 CPU 的每个核支持 8 个线程。这种支持意味着,可以将多个线程加载到处理核以便快速切换。毫无疑问,多核计算机将继续增加多核数量和硬件线程支持。
多核系统趋势继续迫使系统设计人员和应用程序开发人员更好地使用多个计算核。操作系统设计人员必须编写调度算法利用多个处理核以便允许并行执行,如图 2 所示。对于应用程序开发人员来说,挑战是修改现有程序和设计新的程序以便利用多线程。
一般而言,多核系统编程有五个方面的挑战:
由于这些挑战,许多软件开发人员认为,多核系统的出现将需要一个全新方法来设计未来软件系统。同样,许多计算机科学教育者也认为:软件开发课程应当强调平行编程。
通常,有两种类型的并行:数据并行和任务并行。
数据并行注重将数据分布于多个计算核上,并在每个核上执行相同操作。例如,考虑一下对大小为 N 的数组的内容进行求和。对于单核系统,一个线程只能简单相加元素 [0]…[N/2-1]。不过,对于双核系统,线程 A 运行在核 0 上,相加元素 [0]…[N/2-1];而线程 B,运行在核 1 上,相加元素 [N/2]…[N-1]。这两个线程可并行运行在各自的计算核上。
任务并行涉及将任务(线程)而不是数据分配到多个计算核。每个线程都执行一个独特的操作。不同线程可以操作相同的数据,或者也可以操作不同的数据。再考虑刚才的例子。与那个情况相反,一个并行任务的例子可能涉及两个线程,每个线程对元素数组执行一个唯一的统计操作。再次,线程在单独计算核上并行操作,但是每个执行一个独特操作。
从根本上说,数据并行涉及分布数据到多个核,而任务并行分布多个任务到多个核。然而,在实践中,应用程序很少严格遵循数据或任务并行。在大多数情况下,应用程序混合使用这两个策略。