POSIX 线程详解(二)
创建和结束线程
函数:
pthread_create (thread,attr,start_routine,arg) pthread_exit (status) pthread_attr_init (attr) pthread_attr_destroy (attr) |
创建线程:
- 最初,main函数包含了一个缺省的线程。其它线程则需要程序员显式地创建。
- pthread_create 创建一个新线程并使之运行起来。该函数可以在程序的任何地方调用。
-
pthread_create参数:
- thread:返回一个不透明的,唯一的新线程标识符。
- attr:不透明的线程属性对象。可以指定一个线程属性对象,或者NULL为缺省值。
- start_routine:线程将会执行一次的C函数。
- arg: 传递给start_routine单个参数,传递时必须转换成指向void的指针类型。没有参数传递时,可设置为NULL。
- 一个进程可以创建的线程最大数量取决于系统实现。
- 一旦创建,线程就称为peers,可以创建其它线程。线程之间没有指定的结构和依赖关系。
|
|
Q:一个线程被创建后,怎么知道操作系统何时调度该线程使之运行? A:除非使用了Pthreads的调度机制,否则线程何时何地被执行取决于操作系统的实现。强壮的程序应该不依赖于线程执行的顺序。 |
线程属性:
- 线程被创建时会带有默认的属性。其中的一些属性可以被程序员用线程属性对象来修改。
- pthread_attr_init 和 pthread_attr_destroy用于初始化/销毁先成属性对象。
- 其它的一些函数用于查询和设置线程属性对象的指定属性。
- 一些属性下面将会讨论。
结束终止:
-
结束线程的方法有一下几种:
- 线程从主线程(main函数的初始线程)返回。
- 线程调用了pthread_exit函数。
- 其它线程使用 pthread_cancel函数结束线程。
- 调用exec或者exit函数,整个进程结束。
- pthread_exit用于显式退出线程。典型地,pthread_exit()函数在线程完成工作时,不在需要时候被调用,退出线程。
- 如果main()在其他线程创建前用pthread_exit()退出了,其他线程将会继续执行。否则,他们会随着main的结束而终止。
- 程序员可以可选择的指定终止状态,当任何线程连接(join)该线程时,该状态就返回给连接(join)该线程的线程。
- 清理:pthread_exit()函数并不会关闭文件,任何在线程中打开的文件将会一直处于打开状态,知道线程结束。
- 讨论:对于正常退出,可以免于调用pthread_exit()。当然,除非你想返回一个返回值。然而,在main中,有一个问题,就是当main结束时,其它线程还没有被创建。如果此时没有显式的调用pthread_exit(),当main结束时,进程(和所有线程)都会终止。可以在main中调用pthread_exit(),此时尽管在main中已经没有可执行的代码了,进程和所有线程将保持存活状态。
-
其中线程取消的定义
-
一般情况下,线程在其主体函数退出的时候会自动终止,但同时也可以因为接收到另一个线程发来的终止(取消)请求而强制终止。
线程取消的语义
线程取消的方法是向目标线程发Cancel信号,但如何处理Cancel信号则由目标线程自己决定,或者忽略、或者立即终止、或者继续运行至Cancelation-point(取消点),由不同的Cancelation状态决定。
线程接收到CANCEL信号的缺省处理(即pthread_create()创建线程的缺省状态)是继续运行至取消点,也就是说设置一个CANCELED状态,线程继续运行,只有运行至Cancelation-point的时候才会退出。
取消点
根据POSIX标准,pthread_join()、pthread_testcancel()、pthread_cond_wait()、pthread_cond_timedwait()、sem_wait()、sigwait()等函数以及read()、write()等会引起阻塞的系统调用都是Cancelation-point,而其他pthread函数都不会引起Cancelation动作。但是pthread_cancel的手册页声称,由于LinuxThread库与C库结合得不好,因而目前C库函数都不是Cancelation-point;但CANCEL信号会使线程从阻塞的系统调用中退出,并置EINTR错误码,因此可以在需要作为Cancelation-point的系统调用前后调用pthread_testcancel(),从而达到POSIX标准所要求的目标,即如下代码段:
pthread_testcancel(); retcode = read(fd, buffer, length); pthread_testcancel();
程序设计方面的考虑
如果线程处于无限循环中,且循环体内没有执行至取消点的必然路径,则线程无法由外部其他线程的取消请求而终止。因此在这样的循环体的必经路径上应该加入pthread_testcancel()调用。
与线程取消相关的pthread函数
int pthread_cancel(pthread_t thread)
发送终止信号给thread线程,如果成功则返回0,否则为非0值。发送成功并不意味着thread会终止。int pthread_setcancelstate(int state, int *oldstate)
设置本线程对Cancel信号的反应,state有两种值:PTHREAD_CANCEL_ENABLE(缺省)和PTHREAD_CANCEL_DISABLE,分别表示收到信号后设为CANCLED状态和忽略CANCEL信号继续运行;old_state如果不为NULL则存入原来的Cancel状态以便恢复。int pthread_setcanceltype(int type, int *oldtype)
设置本线程取消动作的执行时机,type由两种取值:PTHREAD_CANCEL_DEFFERED和PTHREAD_CANCEL_ASYCHRONOUS,仅当Cancel状态为Enable时有效,分别表示收到信号后继续运行至下一个取消点再退出和立即执行取消动作(退出);oldtype如果不为NULL则存入运来的取消动作类型值。void pthread_testcancel(void)
检查本线程是否处于Canceld状态,如果是,则进行取消动作,否则直接返回。
#include
<pthread.h>
#include "errors.h"
void *hello_world (void *arg)
{
printf ("Hello world\n");
return NULL;
}
int main (int argc, char *argv[])
{
pthread_t hello_id;
int status;
status = pthread_create (&hello_id, NULL, hello_world, NULL);
if (status != 0)
err_abort (status, "Create thread");
status = pthread_join (hello_id, NULL);
if (status != 0)
err_abort (status, "Join thread");
return 0;
}
mian
函数运行时,系统会自动创建一个线程,称为主线程。通过pthread_create创建的线程,称为子线程。程序中通过pthread_join函数等待子线程返回,让子线程运行打印信息,否则主线程return
0结束该进程,新建的子线程也随即死亡,屏幕上将看不到子线程的输出。