线程调度在多处理器系统上,操作系统通常根据任何给定时间的系统活动在不同的处理器上调度线程。当处理器处于空闲状态时,通常使用调度算法在此处理器上调度线程。新线程准备就绪可供执行时,将根据处理器可用性以及新线程的优先级在调度该线程后直接执行或将其置于就绪的队列中。如果应用程序没有为特定的处理器/核指定“硬关联”(此处称为“关联”),则操作系统调度程序将使用其调度算法在任何可用处理器上调度线程。Microsoft 提供的 API 将线程与某个特定处理器 (SetThreadAffinityMask()) 关联,这指明仅在指示的处理器上执行该线程,即使其他处理器处于空闲状态,也是如此。使用关联可能会导致性能降低,因为关联的线程在等待指定的处理器时可能会被停止,即使系统中的其他处理器处于空闲状态,也是如此。为解决此问题,Microsoft 引入了另一个 API(即 SetIdealProcessor()),这样开发人员可以为操作系统提供有关应首选哪个处理器来执行特定线程的提示。但是,如果首选的处理器不可用,则操作系统会使用调度算法从其他可用的处理器中进行选择。下面的“结果”部分展示了通过将一个或多个线程与单核相关联而进行的研究,可帮助您了解对功率/性能特征的影响。
线程模型从本质上将,有两种类别的线程模型:
- 数据域分解:在此模型中,可用的数据集被分成不同的部分,而且每个线程都在其各个部分中工作。在同步点,线程可以将其各自的工作部分组合在一起。由于每个线程使用不同的数据集执行相同的工作,因此,如果对源代码进行任何更改,此线程模型不大可能对性能有任何影响。在这种情况下所做的更改对两个线程的影响程度是一样的。例如,用两个线程各处理一半可用数据集的数据处理应用程序将对其各自的数据集执行类似的指令。对代码库(新增的用于筛选某些数据的功能)所做的任何更改对两个线程的影响程度都相同,因为这两个线程必须通过该新功能运行。因此,多线程性能不大可能会受到更改的影响。
- 功能域分解:在此模型中,每个线程都在应用程序内的各个代码功能块/段中工作。在同步点,线程通常会将其各自的工作项目组合起来。在这种情况下,由于每个线程都在应用程序内的各个代码段中工作,因此在一个或多个代码段中所做的更改可能会对性能造成严重影响。例如,如果上面论述的数据处理应用程序使用以下方式进行多线程处理,即,一个线程执行数据收集阶段,而另一个线程执行数据处理阶段,则对数据处理(新增的用于筛选某些数据的功能)所做的任何更改都可能会导致两个线程间出现失衡情况,因为数据处理线程运行的时间可能会更长。因此,在这种情况下所做的更改可能会对多线程性能产生影响。
除了数据和功能分解以外,还可以将线程模型进一步分为均衡和不均衡线程类别。
- 均衡线程模型:在这种情况下,每个线程都与应用程序的其他活动线程具有等量的工作。均衡线程的一个示例就是数据处理应用程序(已在上面的“数据域分解”部分中论述了),其中的两个线程通过对其各自的数据集执行类似的指令,分别处理可用数据集的一半。这是开发人员的工作,目的是确保不同线程处理的数据之间没有相关性。数据相关性需要使用同步对象,这会严重降低性能,从而通过使用多线程技术会使得性能改进程度最低。应该始终在线程之间执行最少的同步操作。均衡线程模型产生的效果通常会比不均衡模型产生的效果更好。
- 不均衡线程模型:在这种情况下,应用程序内每个线程所执行的工作量有显著差异。按照上面的“功能域分解”部分中论述的示例,如果数据处理应用程序中的数据收集阶段比数据处理阶段所用的时间更少,则会产生线程不均衡情况。一般情况下,功能线程模型往往会在应用程序中创建不均衡线程,因为每个线程都在不同的代码段中工作,因此可能会导致一个线程比其他线程完成的速度要快。这可能会减少运行并行代码所用的时间,但可能会导致应用程序中出现不均衡情况。
下一部分论述应用程序使用上面论述的一种或多种线程技术执行多线程处理的功率/性能结果。