对传统的UNIX进程来讲,一个进程中只有一个线程,这就意味着一个进程在同一时刻只能做一件事(即使是多核CPU)。使用多线程技术, 我们可以设计程序使得一个进程在同一时刻做多件事。使用多线程编程具有以下优势:

  1. 我们可以以独立线程分别处理其对应事件类型事件的方式来处理异步事件,以此来简化我们的编码。每个线程中就可以以同步编程(顺序编程)的方式来编程了。同步(顺序)编程要比异步编程简单的多。
  2. 多进程程序必须使用操作系统提供的复杂机制来实现共享内存、共享文件描述符等,而相比而言,同一进程中的多线程在这个进程中具有相同的内存地址空间和文件描述符。
  3. 一些问题可以被分割开来以提高程序整体的性能。单线程程序在处理多任务时只能将任务序列化处理,因为它同一时刻只有一个线程在工作。使用多线程控件时,可以通过一个独立线程一个任务的方式来交叉处理多个独立任务。
  4. 同样的,对于交互式程序来说, 使用多线程技术可以大大提高程序的反应速度。

一个线程由一个进程中那些能代表当前执行上下文的所必要的信息组成。它包括一个用于标志线程的线程ID、一个寄存器值的集合、一个堆栈、一个优先级表、一个信号掩码、一个errno变量和一个指定线程数据(thread-specific data)。进程中的所有资源都被这个进程中的所有线程所共享,它包括程序执行上下文、程序的全局和堆内存、堆栈、文件描述符 。我们接下来要引述的线程接口来自 POSIX.1-2001。

线程ID

正如每个进程都有一个进程ID一样,每个线程都有一个线程ID,只不过一个进程的进程ID在系统中全局唯一,而线程ID只在线程所属的进程中有意义。线程ID使用数据类型pthread_t来表示。实现被允许使用结构体来代表pthread_t类型,因此好的实现不应将pthread_t当成整数来对待。因此,我们必须使用函数来比较两个线程ID:

 #include <pthread.h>

 /* Return:nozero if equal, 0 otherwise */
int pthread_equal(pthread_t tid1, pthread_t tid2);

Linux3.2.0使用用无符号长整型实现pthread_t。Solaris 10 使用无符号整形代表pthread_t。FreeBSD 8.0和Mac OS X 10.6.8 使用执行pthread结构体的指针来代表pthread_t。

获取线程ID

 #include <pthread.h>

 /* Return:the thread ID of the calling thread */
pthread_t pthread_self(void);

创建线程

使用pthread,在程序启动的时候一个进程也是仅有一个线程的,在程序运行的时候他与传统的进程没有什么区别, 直到他在进程中创建了更多的多线程。

 #include <pthread.h>

 /* Return: 0 if ok, error number on failure */
int pthread_create(
pthread_t* restrict tidp,
const pthread_attr_t* restrict attr,
void* (* start_rtn)(void*),
void* restrict arg);

tidp 用于获取线程成功创建后的线程Id;attr用户自定各种线程属性;start_rtn指定线程要执行的函数地址;arg为start_rtn指向函数的参数;

新创建的线程可以访问进程地址空间并继承调用线程的浮点环境(floating-point environment)和信号掩码,然而新线程的阻塞信号集是被清空的。注意pthread类函数在失败时通常返回一个错误码而不像其他POSIX函数那样设置errno。每个线程拥有一个errno副本仅仅是为了与现有使用errno的函数兼容。

终止线程

如果一个进程中任何一个线程调用了 exit、_exit或_Exit,那么整个进程会被中止。同样的,向一个线程一个默认处理方式是终止进程的信号会中止这个线程所在的进程。

单个线程可以有以下三种退出方式:

  1. 简单的从启动例程中返回。返回值就是线程的退出码。
  2. 线程可以被所属进程中的另一个线程取消掉。
  3. 线程可以通过调用pthread_exit退出
 #include <pthread.h>

 /*
终止线程并通过rval_ptr返回一个值,
rval_ptr可以被同一进程中调用pthread_join
方法的线程获取到
*/
void pthread_exit(void* rval_ptr); /*
等待thread线程结束,thread必须是joinable的。
如果rval_ptr不为空,它会复制目标线程的退出码(
如目标线程在pthread_exit中提供的值)到rval_ptr
指向的位置。如果目标线程被取消PTHREAD_CANCELED
会被放置到rval_ptr指向的位置
*/
void pthread_join(pthread_t thread, void** rval_ptr);

传递给pthread_exit 和 pthread_create的无类型指针可用于传输复杂类型数据。通过pthread_jion方法我们可以将我们等待的线程置于检测状态(detached state),而此时调用线程就可以发现(discover)被等待线程的资源。应当注意的是,当pthread_exit调用结束时,他的rval_ptr的值仍是有效的。这就意味着如果rval_ptr指向的内存在调用线程的堆栈(Stack)上分配,那么rval_ptr在被使用的时候它指向的内存的内容可能已经改变。举例来说,如果一个线程在它的堆栈上给一个struct结构分配了一块内存,并将struct结构作为参数传递给了pthread_exit函数,那么当pthread_join的调用线程 在使用rval_ptr时,这个结构可能已经被销毁而他指向的内存可能已经用于他处。为了避免这种情况,我们应当使用全局变量或者在堆(Heap)上给结构分配内存。

一个线程可以通过pthread_cancel方法请求取消同一进程中另一线程的执行:

 #include <pthread.h>

 /* Return: 0 if OK,error number on failuer */
int pthread_cancel(pthread_t tid);

默认情况下,调用pthread_cancel 函数会使tid线程的行为就像它自己使用PTHREAD_CANCELED参数调用了pthread_exit一样。线程可以选择忽略或其他的处理方式来处理cancel请求。pthread_cancel不会等待线程结束,它几乎只是发送cancel请求。

线程可以安排在它退出时需要执行的函数,这些函数一般是一些线程清理句柄(thread cleanup handlers) 。一个线程可以建立多个清理句柄,这些句柄存储在堆栈(stack)中,即他们会按注册时的顺序逆序执行。

 #include <pthread.h>

 /* 注册清理函数 */
void pthread_cleanup_push(void (*rtn) (void* ), void* arg); /* 移除栈顶的清理函数,如果excute不是0,将执行清理函数 */
void pthread_cleanup_pop(int excute);

pthread_cleanup_push注册的函数在以下三种情况下会被调用:

  1. 线程调用了 pthread_exit 函数
  2. 线程响应cancel请求
  3. 线程使用非0参数调用了pthread_cleanup_pop函数。

无论pthread_cleanup_pop函数再被调用时是否使用了非0参数,他都会将栈顶的pthread_cleanup_push注册的清理函数移除掉。注意, return并不会执行注册的清理函数,我们不应该在pthread_cleanup_push和pthread_cleanup_pop之间使用return,唯一可行的办法是在他们之间调用pthread_exit。

默认情况下,一个线程的退出状态会一直保留,除非我们对这个线程调用pthread_join函数(调用后线程处于detached 状态)。如果一个线程被distach了,那么当它退出时它的底层存储会被立即回收;可以使用thread_detach函数来detach一个线程:

 #include <pthread.h>

 /* Return: 0 if OK, error number if failure */
int pthread_depatch(pthread_t tid);

线程与进程函数对照表:

进程主要函数 线程主要函数 描述
fork pthread_create 创建新的实例
exit pthread_exit 退出
waitpid pthread_join 等待实例结束并获取结束码
atexit   pthread_cleanup_push 注册推出前要执行的函数
getpid pthread_self 获取实例ID
abort pthread_cancel 请求终止实例

APUE 4 - 线程的更多相关文章

  1. (九) 一起学 Unix 环境高级编程 (APUE) 之 线程

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  2. (十) 一起学 Unix 环境高级编程 (APUE) 之 线程控制

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  3. APUE 4 - 线程<2> : 线程同步

    当控件的多个线程共享统一内存时,我们需要确定各个线程访问到的数据的一致性.在cpu结构中,修改操作由多个内存读写周期(memory cycle),而在这些内存周期之间, 有可能会发生其他线程的内存读操 ...

  4. 【APUE】线程与信号

    每个线程都有自己的信号屏蔽字,但是信号的处理是进程中所有线程共享的.进程中的信号是递送到单个线程的. 线程中pthread_sigmask函数类似与进程的sigprocmask函数,可以用来阻塞信号. ...

  5. [21]APUE:线程同步之记录锁(文件)

    [a] 概念 建议锁:在遵循相同记录锁规则的进程/线程间生效,通常用于保证某个程序自身多个进程/线程间的数据一致性 强制锁:意在保证所有进程间的数据一致性,但不一定有效:如不能应对先 unlink 后 ...

  6. [17]APUE:线程

    通常情况下,线程模型的并发性能优于进程模型,但不总是这样 线程的优势: 线程的创建.销毁及上下文切换代价比进程低 某些情况下,使用线程可以简化逻辑,避免异步编程的复杂性 同一进程内所有线程共享全局内存 ...

  7. (十三) [终篇] 一起学 Unix 环境高级编程 (APUE) 之 网络 IPC:套接字

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  8. (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  9. (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

随机推荐

  1. Be the Best of Whatever You Are

    If you can't be a pine on the top of the hill, Be a scrub in the valley—but be The best little scrub ...

  2. linux ls 命令

    ls 命令是 Linux 下最常用的命令之一,用来查询目录下的内容(list directory contents).本文将介绍其基本的用法和一些典型的用例.笔者使用的测试环境为 ubuntu 16. ...

  3. java面向对象理解

    面向对象:世间一切事物均可认为是对象,用户不必了解软件内部的实现机制,可根据需要直接调用接口,生成一个正常工作的应用程序. 面向对象的特点:抽象,封装,继承,多态性, 对象:对象就是一个具有明确行为的 ...

  4. mysql:Linux系统下mysql5.6的安装卸载

    1.1. 下载rpm包 要使用yum 安装mysql,需要mysql的yum仓库,先从官网下载适合你系统的仓库 http://dev.mysql.com/downloads/repo/yum/ 我的是 ...

  5. ios 初体验<UILabel控件>

    创建控件: UILabel *label = [[UILabel alloc]init]; //设置控件大小 label.frame = CGRectMake(50,100,300,40);//分别为 ...

  6. zookeeper原理介绍

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt354 ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,它 ...

  7. 一些LVS实验配置、工具和方案

    最近做了一些LVS配置和方案的验证实验,将过程中用到的一些配置.工具和具体的解决方案记录一下.使用DR模式.验证一种不中断业务的RealServer升级或者重启方案. 网络规划: 节点 IP地址 ce ...

  8. 团队作业4——第一次项目冲刺(Alpha版本)2017.4.28

    2017.04.28 天气晴朗 东风3级. 时间:上午 9:35 ---10:10分 地点:陆大二楼 会议内容:实验室报修系统项目冲刺Alpha版的的最后一天,大家对现在项目的进程进行了讨论,阐述了各 ...

  9. 团队作业4——第一次项目冲刺(Alpha版本) 4.23

    团队作业4--第一次项目冲刺(Alpha版本) Day two: 会议照片 每日站立会议: 项目进展 今天是项目的Alpha敏捷冲刺的第二天,先大概整理下昨天已完成的任务以及今天计划完成的任务.今天主 ...

  10. 201521123074 《Java程序设计》第5周学习总结

    1.本周学习总结 1.1 尝试使用思维导图总结有关多态与接口的知识点. 1.2 可选:使用常规方法总结其他上课内容. 接口定义了解:接口(interface)就是方法声明和常量值的集合. 几种接口讲解 ...