Pthread - POSIX线程编程

线程的动机是充分利用多核,方便地实现并行编程。然而,早期的硬件厂商纷纷实现自己的一套线程标准,这样,程序员无法开发可移植的软件。为了结束这个局面,对UNIX系统,IEEE制定了一个C语言线程编程接口标准(IEEE POSIX 1003.1c)。参考这个标准的实现就被称作POSIX threads 或者 Pthreads.

什么是线程?

在理解线程之前,可以考察一个UNIX进程。一个进程由操作系统创建,包含一些程序资源和运行状态的信息,包括:

  • 进程ID, 进程组ID, 用户ID和组ID
  • 运行时环境
  • 运行时目录
  • 程序指令序列
  • 寄存器
  • 文件句柄
  • 信号量动作
  • 共享库
  • 程序间通信的一些技术和工具(例如消息队列,管道和共享内存)

线程是一些独立的指令流,并且可以被操作系统调度。指令流执行的控制之所以是独立的,并且能被调度,源于线程有独立的:

  • 栈指针
  • 寄存器
  • 调度属性(例如:策略和优先级)
  • 挂起和阻塞的信号量集合
  • 线程相关的数据

总而言之,在UNIX中,一个线程:

  • 存在于进程中,可以使用进程的资源
  • 只要宿主进程存在,并且有操作系统的支持,一个线程就可以有独立的控制流程
  • 和其他独立(或有依赖)的线程共享进程的资源
  • 如果宿主进程退出,线程也会退出
  • 线程是轻量级的是因为负载已经由宿主进程承担

因为多个线程存在于一个进程中,这会导致下面几种情况:

扫描二维码关注公众号,回复: 1263988 查看本文章
  • 某个线程改变了进程中的某个资源(例如打开一个文件,或关闭一个文件)会被其他线程观察到
  • 多线程可以拥有对同一个数据的指针
  • 多个线程可以访问共享内存,所以,需要开发人员做显式的同步操作

为什么要引入Pthreads

Pthreads的一个主要目标是提升程序的性能。创建线程和线程的调度并不会占用操作系统很多的负载。进程中的所有线程共享相同的地址空间,所以,线程之间的通信相比较进程会更高效和容易。多线程应用可以通过下面的途径达到性能上的提升:

  • CPU和I/O同时工作。例如在一个有CPU密集型任务的进程中,当在等待I/O结束的时候,另外的线程可以占用CPU,执行那些耗费CPU的任务。
  • 优先级调度。重要的任务可抢占低优先级的执行。
  • 异步的事件调度。例如:一个web服务器在传输上一个请求的数据的同时,还可以管理接收新的请求。

设计多线程的程序

在现代多核CPU的系统中,pthreads很优雅地适合并行编程。实际上,在进行并行编程的过程中,需要有诸多考虑:

  • 使用哪种并行编程模型
  • 问题划分
  • 负载均衡
  • 线程之间的通信
  • 数据依赖
  • 同步和条件竞争
  • 内存操作
  • I/O操作
  • 程序复杂度
  • 程序员的付出和时间

通常来讲,为了充分利用多线程带来的益处,一个程序员必须将程序划分为离散的,互相独立的任务,这些任务可以兵符执行。比如说,routine1和routine2的执行可以互相重叠,那么,就可以利用多线程。通常,拥有下面特点的程序就适合使用多线程:

  • 多个任务可以并发执行某个工作,或者操作某项数据
  • 可能会被很长的I/O操作阻塞
  • 必须响应多个异步事件
  • 某些工作比另外的更加重要(使用优先级中断)

有几个通用的多线程模式:

  • 管理者/工作者:一个被称作管理者的独立线程将任务委派给多个工作线程。通常情况下,管理者线程会维护所有的输入。至少有两类通用的管理者/工作者模型:静态的工作者线程池和动态的工作者线程池。
  • 管道:一个任务可以划分为多个子操作。每一个子操作被不同的线程执行,但是,整个任务还是序列化执行的。汽车装配流水线很形象地描述了这中模式。
  • 同行:类似于管理者/工作者模型,不同的是,一个主线程创建了其他线程后,主线程也会参与任务的执行。

线程管理

创建和终止线程的接口有下面几种:

pthread_create (thread,attr,start_routine,arg)
pthread_exit (status)
pthread_cancel (thread)
pthread_attr_init (attr)
pthread_attr_destroy (attr)

一开始,main方法默认会创建一个独立的线程。其他的线程必须由程序员显式创建。可以在任何时候和任何地方创建一个新的线程。被创建的线程也可以创建新的线程。

Pthreads提供了多个接口被用于线程定义如何调度线程。例如,可以指定线程的调度使用FIFO(first in first out), RR(round-robin)或者OTHER(由操作系统来控制)的调度方案。Pthreads还提供了设置线程优先级的接口。

线程终止有多种途径:

  • 线程正常结束自己的工作并返回。
  • 线程调用了pthread_exit接口。
  • 其他线程调用pthread_cancel取消该线程的运行。
  • 宿主进程调用exec或者exit结束整个进程的运行。
  • main方法结束。

pthread_exit并不会关闭打开的文件句柄,任何在线程中打开的文件都会一直保持打开状态,尽管线程已经中止。


猜你喜欢

转载自bofang.iteye.com/blog/1679960