APUE 4 - 线程
对传统的UNIX进程来讲,一个进程中只有一个线程,这就意味着一个进程在同一时刻只能做一件事(即使是多核CPU)。使用多线程技术, 我们可以设计程序使得一个进程在同一时刻做多件事。使用多线程编程具有以下优势:
- 我们可以以独立线程分别处理其对应事件类型事件的方式来处理异步事件,以此来简化我们的编码。每个线程中就可以以同步编程(顺序编程)的方式来编程了。同步(顺序)编程要比异步编程简单的多。
- 多进程程序必须使用操作系统提供的复杂机制来实现共享内存、共享文件描述符等,而相比而言,同一进程中的多线程在这个进程中具有相同的内存地址空间和文件描述符。
- 一些问题可以被分割开来以提高程序整体的性能。单线程程序在处理多任务时只能将任务序列化处理,因为它同一时刻只有一个线程在工作。使用多线程控件时,可以通过一个独立线程一个任务的方式来交叉处理多个独立任务。
- 同样的,对于交互式程序来说, 使用多线程技术可以大大提高程序的反应速度。
一个线程由一个进程中那些能代表当前执行上下文的所必要的信息组成。它包括一个用于标志线程的线程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,那么整个进程会被中止。同样的,向一个线程一个默认处理方式是终止进程的信号会中止这个线程所在的进程。
单个线程可以有以下三种退出方式:
- 简单的从启动例程中返回。返回值就是线程的退出码。
- 线程可以被所属进程中的另一个线程取消掉。
- 线程可以通过调用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注册的函数在以下三种情况下会被调用:
- 线程调用了 pthread_exit 函数
- 线程响应cancel请求
- 线程使用非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 - 线程的更多相关文章
- (九) 一起学 Unix 环境高级编程 (APUE) 之 线程
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- (十) 一起学 Unix 环境高级编程 (APUE) 之 线程控制
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- APUE 4 - 线程<2> : 线程同步
当控件的多个线程共享统一内存时,我们需要确定各个线程访问到的数据的一致性.在cpu结构中,修改操作由多个内存读写周期(memory cycle),而在这些内存周期之间, 有可能会发生其他线程的内存读操 ...
- 【APUE】线程与信号
每个线程都有自己的信号屏蔽字,但是信号的处理是进程中所有线程共享的.进程中的信号是递送到单个线程的. 线程中pthread_sigmask函数类似与进程的sigprocmask函数,可以用来阻塞信号. ...
- [21]APUE:线程同步之记录锁(文件)
[a] 概念 建议锁:在遵循相同记录锁规则的进程/线程间生效,通常用于保证某个程序自身多个进程/线程间的数据一致性 强制锁:意在保证所有进程间的数据一致性,但不一定有效:如不能应对先 unlink 后 ...
- [17]APUE:线程
通常情况下,线程模型的并发性能优于进程模型,但不总是这样 线程的优势: 线程的创建.销毁及上下文切换代价比进程低 某些情况下,使用线程可以简化逻辑,避免异步编程的复杂性 同一进程内所有线程共享全局内存 ...
- (十三) [终篇] 一起学 Unix 环境高级编程 (APUE) 之 网络 IPC:套接字
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
随机推荐
- JSP慕课网阶段用户登录小例子(不用数据库)
getAttribute和setAttribute一起使用,而getParameter用于取得如request传来的参数. Web是请求/响应架构的使用,而request和response就是在服务器 ...
- Keep in Mind
Do not undermine your worth by comparing yourself with others.It is because we are different and eac ...
- MongoDB-python的API手记
-------------------python调用MongoDB------------------- 1.官方文档:http://api.mongodb.org/python/current/t ...
- Bootstrap-table使用记录(转)
HTML代码 /*index.cshtml*/ @section styles{ <style> .main { margin-top:20px; } .modal-body .form- ...
- Redis单机版和集群版的安装和部署
1.单机版的安装 本次使用redis3.0版本.3.0版本主要增加了redis集群功能. 安装的前提条件: 需要安装gcc:yum install gcc-c++ 1.1 安装redis 1.下载re ...
- [ASP.NET MVC] Razor 布局
整体视图模板 View模板会先找到对应的controller文件,再找此文件下的Shared文件夹. 比如项目最外层的View/Shared 目录下有一个_Layout.cshtml模板页,有这样代码 ...
- 图像处理:卷积模块FPGA 硬件加速
本文记录了利用FPGA加速图像处理中的卷积计算的设计与实现.实现环境为Altera公司的Cyclone IV型芯片,NIOS II软核+FPGA架构. 由于这是第一次设计硬件加速模块,设计中的瑕疵以及 ...
- VHDL学习:利用Quartus自带库3步快速完成状态机
Quartus自带库里面有各种编程语言的模板,供开发者参考. 初学者利用VHDL实现状态机比较生疏的情况下,可以调出该模板,适当修改即可. 本文将描述如何利用Quartus自带库调出状态机模板,并适当 ...
- 201521123007《Java程序设计》第8周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容. 2. 书面作业 本次作业题集集合 1. List中指定元素的删除(题目4-1) private static ...
- 201521123018 《Java程序设计》第2周学习总结
1. 本章学习总结 * String类 * Java数组Array的使用 2. 书面作业 Q1.(1) 使用Eclipse关联jdk源代码,并查看String对象的源代码(截图)? (2)分析Stri ...