确定要并行处理的块后,便可以创建应用程序的线程。可以使用多种方法对代码进行线程处理。例如,可以使用目标操作系统提供的 API 或跨平台 API。另外,还可以选择使用 OpenMP*,该方法在编译器内提供内置线程处理支持。上述方法中最简单的一种方法是 OpenMP,此方法注重线程创建和同步。某些编译器(如英特尔® C++ 编译器)提供此支持。
OpenMP“段”
OpenMP 段指令是一种对不同的功能块快速执行线程处理的方法。图 4 中的代码段显示了将四个不同功能的执行过程分解到两个线程中的简单程度。在这里,仅用了三行代码(大括号不计算在内)对这些功能进行线程处理。
尽管这是一种执行功能线程的便捷方式,但还是有缺陷:OpenMP 创建的线程数限于定义的段组数或可用的处理器数之间较少的那一个。因此,即使该示例中的代码已在包含四个处理器的系统中运行了,也还是仅使用两个处理器。相反,如果段块比处理器多,则 OpenMP 将确定如何调度要执行的块,这可能不是最理想的。
该示例中的屏障显示应用程序在等待所有线程完成之前所处的位置。
请注意,还可以使用 OpenMP 创建不安全的线程代码,编程人员必须考虑所有线程处理条件(即使使用 OpenMP,也是如此)。
英特尔的任务队列
英特尔 C++ 编译器包括对 OpenMP 规范的扩展,允许对线程池内的任务进行排队。这样一来,如果您具备的可并行化功能数与处理器数一样多时,就可以更容易地利用系统中的所有可用处理器。
图 5 中的代码显示如何任务排队对功能进行线程处理。每个任务指令都会将相应的功能置于队列中。某个处理器可用时,就会在该处理器上执行队列中的下一个功能。
这样一来,将目标锁定在数量可变的处理器上就容易多了。但是,此方法需要更好地规划如何将功能发放到队列,因为运行并行化段的处理器数目是未知的。最终,此方法将帮助 OpenMP 代码段充分利用所有可用的处理器。