一 Pthread 并发编程——深入剖析线程基本元素和状态( 四 )

  • PTHREAD_CANCEL_DEFERRED:当一个线程的取消状态是这个的时候,线程的取消就会被延迟执行 , 知道线程调用一个是取消点的(cancellation point)函数,比如 sleep 和 pthread_testcancel ,所有的线程的默认取消执行的类型就是这个类型 。
  • PTHREAD_CANCEL_ASYNCHRONOUS:如果线程使用的是这个取消类型那么线程可以在任何时候被取消执行,当他接收到了一个取消信号的时候 , 马上就会被取消执行,事实上这个信号的实现是使用 tgkill 这个系统调用实现的 。
事实上我们很少回去使用 PTHREAD_CANCEL_ASYNCHRONOUS,因为这样杀死一个线程会导致线程还有很多资源没有释放,会给系统带来很大的灾难,比如线程使用 malloc 申请的内存空间没有释放 , 申请的锁和信号量没有释放,尤其是锁和信号量没有释放,很容易造成死锁的现象 。
有了以上的知识基础我们现在可以来谈一谈前面的两个函数了:
  • pthread_cancel(t) :是给线程 t 发送一个取消请求 。
  • pthread_testcancel():这个函数是一个取消点,当执行这个函数的时候,程序就会取消执行 。
现在我们使用默认的线程状态和类型创建一个线程执行死循环,看看线程是否能够被取消掉:
#include <stdio.h>#include <pthread.h>#include <assert.h>#include <unistd.h>void* task(void* arg) {while(1) {}return NULL;}int main() {void* res;pthread_t t1;pthread_create(&t1, NULL, task, NULL);int s = pthread_cancel(t1);if(s != 0) // s == 0 mean call successfullyfprintf(stderr, "cancel failed\n");pthread_join(t1, &res);assert(res == PTHREAD_CANCELED);return 0;}在上面的代码当中我们启动了一个线程不断的去进行进行死循环的操作 , 程序的执行结果为程序不会终止 , 因为主线程在等待线程的结束 , 但是线程在进行死循环,而且线程执行死循环的时候没有调用一个是取消点的函数 , 因此程序不会终止取消 。
下面我们更改程序,将线程的取消类型设置为 PTHREAD_CANCEL_ASYNCHRONOUS,在看看程序的执行结果:
#include <stdio.h>#include <pthread.h>#include <assert.h>#include <unistd.h>void* task(void* arg) {pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);while(1) {}return NULL;}int main() {void* res;pthread_t t1;pthread_create(&t1, NULL, task, NULL);int s = pthread_cancel(t1);if(s != 0) // s == 0 mean call successfullyfprintf(stderr, "cancel failed\n");pthread_join(t1, &res);assert(res == PTHREAD_CANCELED);return 0;}在上面的程序当中我们在线程执行的函数当中使用 pthread_setcanceltype 将线程的取消类型设置成 PTHREAD_CANCEL_ASYNCHRONOUS 这样的话就能够在其他线程使用 pthread_cancel 的时候就能够立即取消线程的执行 。
int pthread_setcanceltype(int type, int *oldtype)上方是 pthread_setcanceltype 的函数签名,在前面的使用当中我们只使用了第一个参数,第二个参数我们是设置成 NULL,第二个参数我们可以传入一个 int 类型的指针,然后会在将线程的取消类型设置成 type 之前将前一个 type 拷贝到 oldtype 所指向的内存当中 。
type: 有两个参数:PTHREAD_CANCEL_ASYNCHRONOUS 和 PTHREAD_CANCEL_DEFERRED。
int pthread_setcancelstate(int state, int *oldstate);设置取消状态的函数签名和上一个函数签名差不多,参数的含义也是差不多 , type 表示需要设置的取消状态,有两个参数:PTHREAD_CANCEL_ENABLE 和 PTHREAD_CANCEL_DISABLE,参数 oldstate 是指原来的线程的取消状态,如果你传入一个 int 类型的指针的话就会将原来的状态保存到指针指向的位置 。
其实关于线程的一些细节还有比较多的内容限于篇幅,在本篇文章当中主要给大家介绍这些细节 。
关于栈大小程序的一个小疑惑在上文当中我们使用了一个小程序去测试线程的栈空间的大?。⑶掖蛴『?func 的调用次数,每一次调用的时候我们都会申请 1MB 大小的栈空间变量 。现在我们看下面两个程序 , 在下面两个程序只有 func 函数有区别,而在 func 函数当中主要的区别就是:
  • 在第一个程序当中是先申请内存空间,然后再打印变量 times 的值 。
  • 在第二个程序当中是先打印变量 times 的值,然后再申请内存空间 。
【一 Pthread 并发编程——深入剖析线程基本元素和状态】#include <stdio.h>#include <pthread.h>#include <stdlib.h>#include <sys/types.h>#include <unistd.h>int times = 1;// 先申请内存空间再打印void* func(void* arg) {char s[1 << 20]; // 申请 1MB 内存空间(分配在栈空间上)printf("times = %d\n", times);times++;func(NULL);return NULL;}int main() {pthread_t t;pthread_create(&t, NULL, func, NULL);pthread_join(t, NULL);return 0;}

推荐阅读