本文翻译自POSIX Threads Programming,原作者是:Blaise Barney, Lawrence Livermore National Laboratory
翻译缘由:某次面试面试官的问题,让我一直在思考如何高效的使用线程及其锁,思考很长一段时间没有答案,缺乏实际的负载和场景让我能够真切体会和验证。网上关于锁性能和线程的问题并不多,往往只是简单的介绍编程接口,并在假设的场景下对锁的使用进行介绍,在IBM开发者社区看到一些文章,但是也是比较浅显,没有太直接深入。偶然的机会找到这篇文章,觉得还不错,因此将它翻译出来,一是自己能够从中学习到一些东西和加深理解,另外一点顺道巩固英语(雅思囧)。
目录
- 摘要
- 线程概述
2.1 线程是什么?
2.2 Pthreas是什么?
2.3 是什么使用线程?
2.4 线程编程的设计 - Pthreads API
- 编译线程程序
- 线程管理
5.1 创建和终止线程
5.2 传递参数给线程
5.3 线程结合(Joing)和分离
摘要
在共享内存多处理器架构下,线程可以用来实现并行。硬件厂商会以他们自己私有的方式实现线程,这使得可移植性成为开发人员关心的问题。对于UNIX系统而言,IEEE POSIX 1003.1c标准将c语言线程编程接口标准化了,这个标准的具体实现就是POSIX threads或者Pthreads.
这份材料首先介绍了使用线程需要了解的概念、出发点(动机)和设计方面的考虑。然后,对于三类主要的Pthreads API例程进行了介绍:线程管理,互斥量(Mutex Variables)和条件变量(Condition Variables)。通过示例代码的演示,线程编程新手可以快速学会如何使用这些接口。这份材料包含了对LLNL细节以及MPI和线程混合编程的讨论。作为练习,许多示例代码(c语言实现)也包括在内。
适合的读者/先决条件:这份材料主要是针对那些刚刚接触并行编程的人。阅读这份材料需要对C中并行编程有个基本的了解。对于那些对并行编程并不了解的人,可以参考EC3500: Introduction to Parallel Computing。
Pthreads概览
线程是什么?
- 严格的来说,一个线程被定义为由操作系统调度的一个独立的指令流。这意味着什么?
- 对于开发人员而言,或许对线程最好的描述是:一个独立于它的主程序(main program)运行的“程序”(procedure)。
- 在深入一步,想象一下一个主程序(a.out)包含多个程序(procedures),然后这些程序可以被操作系统同时或者独立的运行,这就是对多线程程序的描述。(译者注:其实可以理解为一个程序有多条不同的执行路径在同时执行。)
- 这是怎么做到的呢?
- 在理解线程之前,首先需要理解UNIX进程。一个进程有操作系统创建,这需要一定的开销(译者注:需要加载可执行文件,构建运行时环境,创建内存空间地址及其映射,打开三个标准的输入输出等等。具体可以参考《程序员的自我修养》)。进程包含了程序的资源以及执行状态,有:
- 线程ID,线程组ID,用户ID,以及用户组ID
- 环境
- 工作目录
- 程序执行
- 寄存器
- 栈
- 堆
- 文件描述符
- 信号执行函数
- 共享库
- 进程间通信的工具(比如消息队列,pipes,信号量或者是内存共享)
UNIX进程
包含线程的UNIX进程
- 线程使用并且与进程的这些资源一同存在,还能够成为操作系统调度和运行的实体。这是因为线程中仅仅包含独立运行需要的资源。
- 线程能够成为独立的执行和调度单位,是因为线程自己维护了:
- 栈指针
- 寄存器
- 调度的属性(比如策略或者优先级)
- 包含处理和阻塞的信号集
- 线程特定的数据
- 总结,对于一个UNIX环境下的线程:
- 存在于某个特定的进程之内,并且使用这个特定进程的资源
- 拥有自己独立的控制流只要他的父进程存在并且操作系统支持
- 仅仅包含独立调度所需的资源
- 与其他线程共享父进程的资源
- 父进程消亡时,线程也消亡
- 轻量级。因为创建进程需要完成更多资源分配,而线程仅仅包含很少的资源。
- 因为同一个进程中的线程共享资源:
- 一个线程修改共享的资源能够被其他线程知道
- 两个指针有相同的值并指向相同的数据
- 能够读写相同的内存地址,因此需要开发者指定同步方式
Pthreads是什么?
- 一直以来,硬件厂商以他们自己的方式实现了线程。不同厂商之间的实现基本是不同的,这就使得开发者很难使用线程开发出基于移植性的程序。
- 为了能够有效利用线程带来的好处,需要一份编程标准:
- 对于UNIX系统,这些接口由IEEE POSIX 1003.1c标准指定(1995)
- POSIX或者Pthreads就是对这个标准的实现
- 现在大多数的硬件厂商除了他们私有的API外,都已经提供Pthreads
- POSIX标准在不断的演变和修改,包括Pthreads规范
- 一些有用的链接
- Pthreads被定义为一组C语言编程接口和调用,通过pthread.h头文件和一个线程库实现——在某些实现中,这个线程库或许是其他库的一部分,比如libc.
为什么使用线程?
->轻量级:
- 与创建进程相比,线程创建仅仅更少开销,对线程的管理需要更少的系统资源。
- 比如,下面的表格比较了fork()和pthread_create()两个子例程创建50, 000个进程和线程的开销。使用time命令测试所需的时间,以秒为单位,不对优化参数进行编译。
注意:通过将系统时间和用户时间相加得到真实的运行时间是不合理的,因为对于SMP系统中多CPUs/cores是在同一时间处理用一个问题。充其量而言,这些结果接近于在本地机器过去和现在运行的结果。
Foo |
1 | //============================================================================== |
1 | //============================================================================== |