并行编程
在现代多核平台上Pthreads十分适用于并行编程,任何需要并行编程的应用领域,都可以用Pthreads进行编程生成相关应用。
设计并行应用程序时需要考虑到多个方面,如下所示:
- 并行编程模型
- 问题分解
- 负载均衡
- 通信
- 数据依赖
- 同步/竞争条件
- 内存
- I/O
- 程序复杂度
- 程序功能/性能/耗时
讨论这些内容已经超出了本教程的范围,感兴趣的读者可以阅读 Introduction to Parallel Computing获得快速概览。
总的来说,如果想要最大化发挥Pthreads的能力,程序需要能够分解为分离、可独立运行的子任务。如果两个任务在时间轴上是可交换的,互相交错的,那么它们可以用各自的线程来执行,如下图所示。
具备如下特性的程序有可能适合使用Pthreads编程:
- 可能因为长时间的I/O操作而阻塞
- 在某个运行阶段会使用比较多的计算资源
- 必须处理一些异步事件
- 一些任务比其他任务更加重要(优先级打断)
目前已经存在了一些多线程编程模型,如下所示:
- 管理者/工作者(manager/worker):一个管理线程负责分配工作给其他工作线程,管理线程一般会处理所有输入并把相关工作打包给工作线程。至少有两类相关的常见模型:静态工作线程池和动态工作线程池
- 流水线(pipeline):一个任务被分解为多个子操作,每个子操作被单独的线程执行,汽车流水线的生产模式和其十分相像。
- 大众模式(Peer):和管理者/工作者模式很像,只不过当主线程创建了子线程之后,主线程也会参与执行一些具体任务。
共享内存模型
所有的线程可以访问全局共享内存,同时它们也有自己的私有数据。编程人员需要管理对全局共享数据的访问和同步,如下图所示。
线程安全
线程安全,简单的说就是一个应用需要能够在不对共享数据产生误操作,或者产生竞争状态的情况下同时执行多个线程任务。
举个栗子,假如你的应用程序创建了几个线程,它们会调用相同的三方库操作:
- 三方库操作中会对某个内存中的数据做访问/修改操作
- 由于多个线程同时调用该操作,可能会出现它们同时尝试修改某一处内存中数据的情况,导致数据不符合预期产生异常
- 此时如果没有一些同步机制来防止数据被异常操作的情况,我们称它不符合线程安全的要求
对于编程人员来说,使用第三方库的影响在于如果你无法100%确定某个操作是线程安全的,那么有可能会带来相关的问题。
所以如果在应用中使用了不确定是否为线程安全的第三方库操作,最好在假设它们不符合线程安全的情况下使用,譬如尽量进行串行调用而非多线程调用。
线程限制
虽然Pthreads API是ANSI/IEEE制定的标准,但在不同平台的具体实现是多样的。因此在一个平台上可以正常运行的程序,在其他平台上有可能会出错。譬如允许的线程最大数量,和线程的默认堆栈大小,这两个重要信息可能在不同平台上有差异,而它们又是设计程序中需要特别注意的信息。
后续教程中会继续讨论相关的信息。