POSIX 线程编程指南(一)

 POSIX线程编程指南

在UNIX/Linux系统中,线程编程是开发应用必须具备的知识,最近看到一篇关于POSIX 线程编程的资料,觉得对初学者有很大的帮组,试着把它翻译出来,原文在下面的链接中:

POSIX Threads Programming:

https://computing.llnl.gov/tutorials/pthreads/ 

 

POSIX Threads Programming

目录

  1. 摘要
  2. Pthreads 概述
    1. 什么是线程Thread?
    2. 什么是Pthreads?
    3. 为什么需要 Pthreads?
    4. 设计 Threaded程序
  3. Pthreads API
  4. 编译 Threaded 程序
  5. 线程管理
    1. 创建和终止线程
    2. 线程中的参数传递
    3. 合并(Joining)与分离(Detaching)线程
    4. 堆栈管理
    5. Miscellaneous Routines
  6. 互斥(Mutex 变量
    1. 互斥量概述
    2. 创建与销毁互斥量
    3. 互斥量的加锁与解锁
  7. 条件变量(Condition Variables
    1. 件变量概述
    2. 创建与销毁条件变量
    3. 等待和触发条件变量
  8. LLNL信息和建议
  9. 未包括的 主题
  10. Pthread 库例程参考
  11. 参考和更多的信息
  12. 练习

 

摘要

在共享内存的多处理器架构中(SMPs),线程可以用来实现并行机制。从前,硬件提供商利用他们自己私有的线程版本。对开发者,为了来实现程序的可移植性。 UNIX系统中, IEEE POSIX 1003.1c 标准定义了一个标准的C语言线程编程接口。遵从此标准的应用被叫做POSIX threads, 或者叫 Pthreads。

本指南介绍了Pthreads的概念、动机和如果设计使用它。 Pthreads API包括三个主要方面:线程管理、互斥和条件变量。在整个过程中,面向一个新的Pthreads开发者而编写了许多示例代码来展示怎样使用Pthreads。包括讨论和示例在IBM SMP 环境中,怎样开发hybrid MPI/Pthreads 程序。作为试验练习,也给出了各种C语言示例源代码。

水平/要求:对于应用线程进行并行编程的初学者,需要了解基本的C语言并行编程知识, 对于不太了解的,阅读EC3500: Introduction To Parallel Computing 会有很大的帮助。

Pthreads 概述

什么是Thread?

  • 从技术的角度, 线程被定义为能被操作系统调度的一个独立的指令流,但是这意味着什么啦?
  • 对于软件开发者,独立于主程序运行的"procedure"可能更好表述线程。
  • 进一步,想象主程序(a.out)包括一定数量的程序(procedures),而且所有这些procedures能够被操作系统调度,从而能同时和/或者独立的运行,那就是所说的”多线程(multi-threaded)“编程。
  • 怎么实现多线程(multi-threaded)“编程啦?
  • 在理解线程之前,首先需要理解UNIX进程。 进程由操作系统创建,需要相当数量的花销"overhead" 进程包括程序资源和程序执行状态相关的信息,包括:
    • 进程 ID, 进程组 ID, 用户 ID, 和组 ID
    • 工作环境(Environment)
    • 工作目录(Working directory).
    • 程序指令(Program instructions)
    • 寄存器(Registers)
    • 栈(Stack)
    • 堆(Heap)
    • 文件描述符(File descriptors)
    • 信号(Signal actions )
    • 共享库(Shared libraries)
    • 进程间通信手段(Inter-process communication tools) (比如:消息队列、管道、信号灯或者共享内存

 

UNIX PROCESS

THREADS WITHIN A UNIX PROCESS

  • 线程存在于进程空间并使用进程的资源,能够被操作系统调度,可以作为一个独立的可执行入口,因为他们仅仅复制了保证他们能够成为可执行代码所必须的资源。
  • 为实现独立的控制流,线程维护自己的:
    • 堆栈指针(Stack pointer)
    • 寄存器(Registers )
    • 调度属性(Scheduling properties)(比如调度策略或者优先级)
    • 信号
    • 线程私有数据 Thread specific data
  • 简而言之,在UNIX环境下的线程:
    • 存在于一进程中,并且使用该进程资源。
    •  在进程生存周期中,在OS的支持下,有自己独立的控制流程。
    •  仅仅复制能够独立调度而必须的资源。
    • 可以与其他线程一起独立(非独立)的共享同一进程资源。
    •  如果父进程死亡或者类似的情况下,线程将会随之消亡。
    •  是轻量级(lightweight)进程,因为大部分开销在创建进程时已经完成(Is "lightweight" because most of the overhead has already been accomplished through the creation of its process.)。
  •  因为线程在同一进程中并且共享进程资源:
    • . 一个线程改变共享资源( 比如关掉一个文件),另外所有的线程能够看到
    • 两个指针指向同一数据。
    • 读写同一内存成为可能,因此需要程序员利用明确的同步机制。

 

Pthreads  Overview

什么是Pthreads?

  • . 在历史上,硬件厂商主要使用他们自己的私有版本的线程库,这些实现的差异非常大,以致于开发者想发出能够移植的线程应用非常困难。
  •  为了能够最大限度的提高线程的性能,需要一个标准的编程接口。对于UNIX系统,IEEE POSIX 1003.1c standard (1995)定义了这样的接口。遵从此标准实现的线程被叫做POSIX线程,或者Pthreads。大部分硬件厂商现在为他们的私有接口提供额外的Pthreads API。
  • Pthreads定义了一套C语言编程接口和函数调用,包括一个pthread.h头文件和一个线程库,尽管此线程库可能是其他库(比如:libc库)的一部分。
  •  POSIX标准中有几个版本,实际应用中注意你所用的线程库版本,因为不同版本之间的差别会引起问题。

 

Pthreads Overview

为什么选择Pthreads?

  •  使用Pthreads的主要动机是实现程序的可移植性。
  • . 与创建与管理一个进程的开销相比较,一个线程可以利用更少的系统开销来创建,管理这些线程也只需要比进程少的系统资源。

例如:下面的表比较了50000次调用fork()pthreads_create()例程创建进程/线程的时间,单位是秒,没有经过优化。

Note: don't expect the sytem and user times to add up to real time, because these are SMP systems with multiple CPUs working on the problem at the same time. At best, these are approximations.

注意:不能期望系统时间加上用户时间就是real time,因为这是多处理器同时运行的SMP系统。这里只是近似值。

Platform

fork()

pthread_create()

real

user

sys

real

user

sys

AMD 2.4 GHz Opteron (8cpus/node)

41.07

60.08

9.01

0.66

0.19

0.43

IBM 1.9 GHz POWER5 p5-575 (8cpus/node)

64.24

30.78

27.68

1.75

0.69

1.10

IBM 1.5 GHz POWER4 (8cpus/node)

104.05

48.64

47.21

2.01

1.00

1.52

INTEL 2.4 GHz Xeon (2 cpus/node)

54.95

1.54

20.78

1.64

0.67

0.90

INTEL 1.4 GHz Itanium2 (4 cpus/node)

54.54

1.07

22.22

2.03

1.26

0.67

下面是源代码:

 

==============================================================================
C Code 
for  fork() creation test
==============================================================================
#include 
< stdio.h >
#include 
< stdlib.h >
#define  NFORKS 50000

void  do_nothing()  {
int i;
i
= 0;
}


int  main( int  argc,  char   * argv[])  {
int pid, j, status;

for (j=0; j<NFORKS; j++{

  
/*** error handling ***/
  
if ((pid = fork()) < 0 ) {
    printf (
"fork failed with error code= %d ", pid);
    exit(
0);
    }


  
/*** this is the child of the fork ***/
  
else if (pid ==0{
    do_nothing();
    exit(
0);
    }


  
/*** this is the parent of the fork ***/
  
else {
    waitpid(pid, status, 
0);
    }

  }

}
  

==============================================================================
C Code 
for  pthread_create() test
==============================================================================
#include 
< pthread.h >
#include 
< stdio.h >
#include 
< stdlib.h >

#define  NTHREADS 50000

void   * do_nothing( void   * null {
int i;
i
=0;
pthread_exit(NULL);
}
                      

int  main( int  argc,  char   * argv[])  {
int rc, i, j, detachstate;
pthread_t tid;
pthread_attr_t attr;

pthread_attr_init(
&attr);
pthread_attr_setdetachstate(
&attr, PTHREAD_CREATE_JOINABLE);

for (j=0; j<NTHREADS; j++{
  rc 
= pthread_create(&tid, &attr, do_nothing, NULL);
  
if (rc) {              
    printf(
"ERROR; return code from pthread_create() is %d ", rc);
    exit(
-1);
    }


  
/* Wait for the thread */
  rc 
= pthread_join(tid, NULL);
  
if (rc) {
    printf(
"ERROR; return code from pthread_join() is %d ", rc);
    exit(
-1);
    }

  }


pthread_attr_destroy(
&attr);
pthread_exit(NULL);

}


 

  •  所有的线程共享一个地址空间,在许多情况下线程间通信更有效,而且比IPC更容易使用。
  • 线程应用除了获得可移植的性能,与非线程应用相比,还有下面几个优势:
    • 重叠(Overlapping)系统I/O操作例如,一个程序可能需要长时间的I/O操作,当一个线程阻塞在一个系统I/O调用时,其他线程能够获得CPU继续运行。
    • 优先级/实时调度: 更重要的任务可以取代或者中断低优先级的任务而被调度。
    • 异步事件处理: tasks which service events of indeterminate frequency and duration can be interleaved. 不定次数和时间的任务可以被交叉处理,例如:一个WEB服务器可以为前一个请求传送数据的同时,还可以管理新到的请求。
  •  SMP架构中使用Pthreads 的主要动机被认为是获得最佳性能, 特别是一个应用在节点通信中使用MPI的时候。使用Pthreads来代替节点间的数据传输可以有很大性能的提高。
  •  例如:
    •  MPI库通常使用共享内存进行节点间通信,这至少会涉及一次内存拷贝操作(进程到进程)。
    • Pthreads中,因为线程共享同一进程的地址空间,中间没有“内存拷贝”操作和数据传输,使“内存拷贝”操作更多的变成为“缓存到CPU”或者“内存到CPU”(最坏的情况)操作,这样使通信速度更高。
    • 下面列出了一些细节上的比较:

Platform

MPI Shared Memory Bandwidth
(GB/sec)

Pthreads Worst Case
Memory-to-CPU Bandwidth
(GB/sec)

AMD 2.4 GHz Opteron

1.2

5.3

IBM 1.9 GHz POWER5 p5-575

4.1

16

IBM 1.5 GHz POWER4

2.1

4

Intel 1.4 GHz Xeon

0.3

4.3

Intel 1.4 GHz Itanium 2

1.8

6.4

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值