转:POSIX线程的创建与取消—pthreads线程库实例笔记1
2011-02-21 14:30
393 查看
创建线程函数——pthread_create()
Linux线程在核内是以轻量级进程的形式存在的,拥有独立的进程表项,而所有的创建、同步、删除等操作都在核外pthread库中进行。pthread库使用一个管理线程(__pthread_manager(),每个进程独立且唯一)来管理线程的创建和终止,为线程分配线程ID,发送线程相关的信号(比如Cancel),而主线程(pthread_create())的调用者则通过管道将请求信息传给管理线程。
取消线程pthread_cancel()
pthread_testcancel()
pthread_setcancelstate()
pthread_setcanceltype()
什么是取消点(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标准所要求的目标,即如下代码段:
我发现,对于C库函数来说,几乎可以使线程挂起的函数都会响应CANCEL信号,终止线程,包括sleep、delay等延时函数,下面的例子对此会进行详细分析。
实例探讨
实例代码
运行结果:
代码分析:
pthread_create(&tid1, NULL, child1, NULL)创建线程ID为tid1,调用child()过程;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate),将线程tid1线程的取消置为无效;
主进程里 pthread_cancel(tid1)意为发送CANCEL信号取消线程tid1,但此时线程的而取消为无效,线程并不接受取消信号;
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate)将线程取消,但线程并不能立即终止,因为线程类型值为默认的PTHREAD_CANCEL_DEFERRED,即遇到下一个取消点才会终止线程;
那么,取消点在哪儿呢,也就是哪一句终止的线程呢?是sleep(2),它可以阻塞线程相应了取消信号。
如果你将sleep拿掉,会发现线程会无休止的运行下去,如果将sleep换成pthread_testcancel(),会发现线程也不会终止,这个函数不是有检测取消点,如果存在就取消线程的功用吗?懵了一会儿,索性将child1函数的while(1)内所有打印去掉,发现线程可以终止了。原来,无延时的频繁打印,会使pthread_testcancel()无法响应取消信号,解决方法是要么取消打印,要么加上延时。
最后是第13行:
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,&oldtype);//comment 1
这句的作用是将取消类型设置为PTHREAD_CANCEL_ASYNCHRONOUS,即取消请求会被立即响应,加上这句,那么运行结果会变为:
由结果看到,比上次的执行结果在【PTHREAD_CANCEL_ENABLE】后少了一次【child1: I am running. 】打印,可以看出,在线程取消设为有效后,直接终止线程,并不进入下一轮循环。
总结:
创建线程
起初,主程序main()包含了一个唯一的默认线程。程序员必须明确创建所有其它线程;
pthread_create创建一个新的线程并使其执行,这个过程可以在你的代码里的任何地方调用多次;
一个进程可以创建的线程的最大数量是依赖于实现的(The maximum number of threads that may be created by a process is implementation dependent. )。
线程一旦被创建,他们都是同等的,并且也可以创建其它线程。它们之间没有层次体系和依赖关系。
终止线程
一个线程有几种终止的方法:
线程从它的起始程序中返回;
线程调用了pthread_exit()函数;
线程被另一个线程调用pthread_cancel()函数所取消;
整个进程由于调用了exec或exit而退出。
pthread_exit经常被用来明确的退出一个线程。通常,pthread_exit()函数在线程完成工作并不在需要时才被调用;
如果主程序main()在一个线程被创建之前,使用pthread_exit()函数退出结束,另一些线程继续执行。否则,他们在主程序main()结束的时候会自动终止;
清除:pthread_exit()函数不关闭文件,任何在线程中被打开的文件,在线程终止后仍然保持打开;
通常,你可以通过pthread_exit()函数来操作程序的执行到结束的过程,当然,除非你想要传递一个返回值。然而,在主程序main()中,在主程序结束时,它所产生的线程有个显而易见的问题。如果你不用pthread_exit(),当main()结束是,主进程以及所有的线程都会终止。如果在main()中调用了pthread_exit(),主进程和它所有的线程将会继续工作,尽管main()中的所有代码已经被执行了。
#include <pthread.h> int pthread_create( pthread_t* thread, const pthread_attr_t* attr, void* (*start_routine)(void* ), void* arg ); 描述: pthread_create()函数创建一个新的线程,通过线程属性对象attr指定属性。 被创建的线程继承了父线程的信号掩码,且它的等待信号是空的。 参数: thread:NULL,或者指向一个pthread_t对象的指针,指向此函数执行后存储的一个新线程的线程ID。 attr: 指向 pthread_attr_t结构体的指针,此结构体指定了线程的执行属性。 如果attr为NULL,则被设置为默认的属性,pthread_attr_init()可以设置attr属性。 注意:如果在创建线程后编辑attr属性,线程的属性不受影响。 start_routine: 线程的执行函数,arg为函数的参数。 如果start_routine()有返回值,调用pthread_exit(),start_routine()的返回值作为线程的退出状态。 在main()中的线程被调用有所不同。当它从main()返回,调用exit(),用main()的返回值作为退出状态。 arg: 传给start_routine的参数。
Linux线程在核内是以轻量级进程的形式存在的,拥有独立的进程表项,而所有的创建、同步、删除等操作都在核外pthread库中进行。pthread库使用一个管理线程(__pthread_manager(),每个进程独立且唯一)来管理线程的创建和终止,为线程分配线程ID,发送线程相关的信号(比如Cancel),而主线程(pthread_create())的调用者则通过管道将请求信息传给管理线程。
取消线程pthread_cancel()
#include <pthread.h> int pthread_cancel( pthread_t thread ); 描述: pthread_cancel()函数请求目标线程被取消(终止)。 取消类型(type)和状态(state)决定了取消函数是否会生效。 当取消动作被执行,目标线程的取消清除操作会被调用。 当最后的取消清除操作返回,目标线程的线程的析构函数会被调用。 当最后的析构函数返回,目标线程会被终止 (terminated)。 线程的取消过程同调用线程会异步执行。 参数: thread: 欲取消的线程ID,可以通过pthread_create()或者pthread_self()函数取得。
pthread_testcancel()
#include <pthread.h> void pthread_testcancel( void ); 描述:函数在运行的线程中创建一个取消点,如果cancellation无效则此函数不起作用。
pthread_setcancelstate()
pthread_setcanceltype()
#include <pthread.h> int pthread_setcanceltype( int type, int* oldtype ); 描述: pthread_setcanceltype()函数设置运行线程的取消类型为type,并且返回原取消类型与oldtype。 取消类型值: PTHREAD_CANCEL_ASYNCHRONOUS:如果取消有效,新的或者是等待的取消请求会立即执行。 PTHREAD_CANCEL_DEFERRED:如果取消有效,在遇到下一个取消点之前,取消请求会保持等待,默认值。 注意:标注POSIX和C库的调用不是异步取消安全地。 参数: type: 新取消类型 oldtype: 指向该函数所存储的原取消类型的指针。
什么是取消点(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();
我发现,对于C库函数来说,几乎可以使线程挂起的函数都会响应CANCEL信号,终止线程,包括sleep、delay等延时函数,下面的例子对此会进行详细分析。
实例探讨
实例代码
1 #include <stdlib.h> 2 #include <stdio.h> 3 #include <pthread.h> 4 5 void cleanup(void *parm) { 6 printf("Inside cancellation cleanup handler/n"); 7 } 8 void * child1(void *arg) { 9 int oldstate, oldtype; 10 int i = 0; 11 pthread_cleanup_push(cleanup,NULL); 12 printf("/nPTHREAD_CANCEL_DISABLE before/n"); 13 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate); 14 // pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,&oldtype);//comment 1 15 while (1) { 16 i++; 17 printf("child1: I am running. /n"); 18 sleep(2); 19 if (i == 5) { 20 printf("/nPTHREAD_CANCEL_ENABLE/n"); 21 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate); 22 //pthread_testcancel();//comment 2 23 } 24 25 } 26 pthread_cleanup_pop(0); 27 } 28 29 int main(int argc, char *argv[]) { 30 int tid1, rc; 31 void * status = NULL; 32 printf("hello, condition variable test/n"); 33 pthread_create(&tid1, NULL, child1, NULL); 34 sleep(2); 35 printf("/npthread_cancel /n"); 36 pthread_cancel(tid1); 37 38 rc = pthread_join(tid1, &status); 39 if (status != PTHREAD_CANCELED) { 40 printf("/npthread_join failed,status=%d,rc=%d/n", status, rc); 41 return -1; 42 } else 43 printf("/npthread_join succ,status=%d,rc=%d/n", status, rc); 44 printf("/nMain completed!~~/n"); 45 46 return EXIT_SUCCESS; 47 }
运行结果:
hello, condition variable test PTHREAD_CANCEL_DISABLE before child1: I am running. pthread_cancel child1: I am running. child1: I am running. child1: I am running. child1: I am running. PTHREAD_CANCEL_ENABLE child1: I am running. Inside cancellation cleanup handler pthread_join succ,status=-1,rc=0 Main completed!~~
代码分析:
pthread_create(&tid1, NULL, child1, NULL)创建线程ID为tid1,调用child()过程;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate),将线程tid1线程的取消置为无效;
主进程里 pthread_cancel(tid1)意为发送CANCEL信号取消线程tid1,但此时线程的而取消为无效,线程并不接受取消信号;
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate)将线程取消,但线程并不能立即终止,因为线程类型值为默认的PTHREAD_CANCEL_DEFERRED,即遇到下一个取消点才会终止线程;
那么,取消点在哪儿呢,也就是哪一句终止的线程呢?是sleep(2),它可以阻塞线程相应了取消信号。
如果你将sleep拿掉,会发现线程会无休止的运行下去,如果将sleep换成pthread_testcancel(),会发现线程也不会终止,这个函数不是有检测取消点,如果存在就取消线程的功用吗?懵了一会儿,索性将child1函数的while(1)内所有打印去掉,发现线程可以终止了。原来,无延时的频繁打印,会使pthread_testcancel()无法响应取消信号,解决方法是要么取消打印,要么加上延时。
最后是第13行:
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,&oldtype);//comment 1
这句的作用是将取消类型设置为PTHREAD_CANCEL_ASYNCHRONOUS,即取消请求会被立即响应,加上这句,那么运行结果会变为:
hello, condition variable test PTHREAD_CANCEL_DISABLE before child1: I am running. pthread_cancel child1: I am running. child1: I am running. child1: I am running. child1: I am running. PTHREAD_CANCEL_ENABLE Inside cancellation cleanup handler pthread_join succ,status=-1,rc=0 Main completed!~~
由结果看到,比上次的执行结果在【PTHREAD_CANCEL_ENABLE】后少了一次【child1: I am running. 】打印,可以看出,在线程取消设为有效后,直接终止线程,并不进入下一轮循环。
总结:
创建线程
起初,主程序main()包含了一个唯一的默认线程。程序员必须明确创建所有其它线程;
pthread_create创建一个新的线程并使其执行,这个过程可以在你的代码里的任何地方调用多次;
一个进程可以创建的线程的最大数量是依赖于实现的(The maximum number of threads that may be created by a process is implementation dependent. )。
线程一旦被创建,他们都是同等的,并且也可以创建其它线程。它们之间没有层次体系和依赖关系。
终止线程
一个线程有几种终止的方法:
线程从它的起始程序中返回;
线程调用了pthread_exit()函数;
线程被另一个线程调用pthread_cancel()函数所取消;
整个进程由于调用了exec或exit而退出。
pthread_exit经常被用来明确的退出一个线程。通常,pthread_exit()函数在线程完成工作并不在需要时才被调用;
如果主程序main()在一个线程被创建之前,使用pthread_exit()函数退出结束,另一些线程继续执行。否则,他们在主程序main()结束的时候会自动终止;
清除:pthread_exit()函数不关闭文件,任何在线程中被打开的文件,在线程终止后仍然保持打开;
通常,你可以通过pthread_exit()函数来操作程序的执行到结束的过程,当然,除非你想要传递一个返回值。然而,在主程序main()中,在主程序结束时,它所产生的线程有个显而易见的问题。如果你不用pthread_exit(),当main()结束是,主进程以及所有的线程都会终止。如果在main()中调用了pthread_exit(),主进程和它所有的线程将会继续工作,尽管main()中的所有代码已经被执行了。
#include <pthread.h> int pthread_setcancelstate( int state, int* oldstate ); 描述: pthread_setcancelstate() f函数设置线程取消状态为state,并且返回前一个取消点状态oldstate。 取消点有如下状态值: PTHREAD_CANCEL_DISABLE:取消请求保持等待,默认值。 PTHREAD_CANCEL_ENABLE:取消请求依据取消类型执行;参考pthread_setcanceltype()。 参数: state: 新取消状态。 oldstate: 指向本函数所存储的原取消状态的指针。
相关文章推荐
- POSIX线程的创建与取消—pthreads线程库实例笔记1
- POSIX线程的创建与取消—pthreads线程库实例笔记1
- POSIX线程的连接和分离—pthreads线程库实例笔记2
- POSIX线程互斥量的使用——pthreads线程库实例笔记3
- POSIX线程库条件变量的使用——Pthreads线程库实例笔记4
- Quartz.NET 2.0 学习笔记(5) :实例创建Windows服务实现任务调度
- GSON使用笔记(2) -- 反序列化时GSON如何创建对象实例
- python_笔记14_创建类的实例(支持多属性),方法也是属性
- python cookbook第三版学习笔记十二:类和对象(三)创建新的类或实例属性
- POSIX线程的创建和取消
- Quartz.NET 2.0 学习笔记(5) :实例创建Windows服务实现任务调度
- python进阶学习笔记(五)——创建实例属性、初始化实例属性
- Castle ActiveRecord学习笔记一:创建一个实例
- 【VS2010学习笔记】【编程实例】 (在Visual Studio中使用C++创建和使用DLL)
- Quartz.NET 2.0 学习笔记(5) :实例创建Windows服务实现任务调度 Quartz.NET 项目地址 http://quartznet.sourceforge.net/ Quar
- iOS: 学习笔记实例, 用代码控制视图创建与切换
- 设计模式学习笔记:就一句话的创建实例是如何演变成工厂模式的?
- 创建实例和管理服务器笔记1
- openstack学习笔记三 创建第一个实例
- Android中创建对话框(确定取消对话框、单选对话框、多选对话框)实例代码