对传统的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. php 数据访问练习:租房查询页面

    <html> <head> <title></title> <meta charset="UTF-8"/> <li ...

  2. LVS之DR模式实战及高可用性

    author:JevonWei 版权声明:原创作品 LVS-DR实现同网段调度web模式 拓扑环境 网络环境 RS1 RIP 192.168.198.138/24 VIP 192.168.198.10 ...

  3. httpd常用配置

    author:JevonWei 版权声明:原创作品 检查配置文件时,如下提示,则因为没有server的服务名称导致,故设置网站的服务server名称,若没有设置web服务名,主默认解析系统主机名(添加 ...

  4. Android WebView基本使用

    转载请注明出处: http://blog.csdn.net/lowprofile_coding/article/details/77928614 WebView介绍 Android WebView在A ...

  5. 运行mvn install时跳过Test

    1.1 方法一 <project> [...] <build> <plugins> <plugin> <groupId>org.apache ...

  6. grunt之concat、cssmin、uglify

    周末有点懒,跑去看了<智取威虎山>,手撕鬼子的神剧情节被徐老怪一条回忆线就解释过去了,牛到极致尽是这种四两拨千斤的处理方式,手撕加分,四星推荐. --------------------- ...

  7. vue中组件的四种方法总结

    希望对大家有用 全局组件的第一种写法 html: <div id = "app"> <show></show></div> js: ...

  8. JS中的运算符和JS中的分支结构

    JS中的运算符 1.算术运算(单目运算符) + .-.*. /. %取余.++自增 .--自减 +:两种作用,链接字符串/加法运算.当+两边全为数字时,进行加法运算:当+两边有任意一边为字符串时,起链 ...

  9. oop 第三次作业 文件读写

    oop第三次作业 GitHub 对于迟交我感到很抱歉 031602510 体会 这次的用到了之前的文件读写,传参 定义函数有返回值,使代码更加简洁.面向对象的程序设计,在面对函数多元的情况下,编程更加 ...

  10. 201521123060 《Java程序设计》第7周学习总结

    1. 本周学习总结 2. 书面作业 1.ArrayList代码分析 1.1 解释ArrayList的contains源代码 答:contains方法调用indexOf方法,遍历遍历内部用于保存数据的e ...