取消机制——杀死线程

线程的取消机制概念

取消机制相关API

pthread_cancel   取消线程

pthread_setcancelstate  设置线程的取消状态

pthread_setcanceltype  设置线程相应取消命令方式

线程取消机制的完善

完善取消机制相关API

pthread_cleanup_push   注册线程退出处理函数

pthread_cleanup_pop   清除注册的退出处理函数

 

线程的取消机制概念

       所谓先线程的取消机制就是杀死一个线程,跟我们进程当中的异步信号有点相似,在我们的程序中,我们可以给某个指定的线程发送取消指令,当这个线程收到这条取消指令之后,便会退出这条线程,我们把这种机制,称为线程的取消机制。

取消机制相关API

pthread_cancel   取消线程

#include <pthread.h>  
int pthread_cancel(pthread_t thread);

函数功能

  • 给指定的线程发送一个取消指令,该线程默认收到此取消指令后回退出线程(线程运行到取消点函数的时候,哪些是取消点函数可以参照man 7 pthreads)

参数:

  • thread:指定的线程ID

返回值:

  • 成功则返回0,失败则返回一个错误值,errno没有被设置

线程也可以选择不被取消指令所取消,这需要使用到取消状态函数

 

pthread_setcancelstate  设置线程的取消状态

#include <pthread.h>  
int pthread_setcancelstate(int state, int *oldstate);

函数功能

  • 设置线程是否可以被取消指令所取消

参数:

  • state:设置的状态:
    • PTHREAD_CANCEL_ENABLE:使能线程的取消功能,代表线程可以被取消指令所退出
    • PTHREAD_CANCEL_DISABLE:关闭线程的取消功能,代表线程就算接收到取消指令也不会有所动作
  • oldstate:旧的状态值回存放到这个内存中,这个参数可以为NUL

返回值

  • 成功则返回0,失败则返回一个错误值,errno没有被设置

pthread_setcanceltype  设置线程相应取消命令方式

#include <pthread.h>  
int pthread_setcanceltype(int type, int *oldtype);

函数功能

  • 设置线程收到取消指令后是立即退出线程还是遇到取消点函数才退出线程

参数:

  • type:取消指令的响应类型:
    • PTHREAD_CANCEL_DEFERRED:延时响应,代表线程接收到取消指令后,遇到取消点函数才会退出
    • PTHREAD_CANCEL_ASYNCHRONOUS:立即响应,代表线程接收到取消指令后,马上退出,就算没有遇到取消点函数。
  • oldstate:原本设置的取消指令的响应类型,他会存放到这个内存中,可以设置为NULL

返回值

  • 成功则返回0,失败则返回一个错误值,errno没有被设置

取消点函数:

线程取消机制的完善

         由于线程是可以被取消(杀死),而且是异步的操作(你不知道什么时候会接收到线程的取消指令),从而导致我们再线程的操作当中容易出现逻辑漏洞(类似于死锁等等),所以我们诞生一种包裹机制,可以在进行某部分逻辑操作的时候,为了防止线程被异步杀死所出现的漏洞,在线程取消之前先去做好一些动作,也就是线程退出处理函数(类似于进程调用exit退出前可以去运行atexit或者是on_exit两个注册的退出处理函数)。

我们可以理解成为就是线程的取消指令的处理函数。

完善取消机制相关API

pthread_cleanup_push   注册线程退出处理函数

pthread_cleanup_pop   清除注册的退出处理函数

#include <pthread.h>  
void pthread_cleanup_push(void (*routine)(void *),void *arg); 
void pthread_cleanup_pop(int execute);

函数功能

  • pthread_cleanup_push:注册一个收到取消指令后的线程退出处理函数,用于执行该函数过后,如果接收到线程的取消指令,则先去执行该函数所注册的函数后,才能退出线程
  • pthread_cleanup_pop:必须与上面的函数配套使用,用于清除出注册的函数,代表以后接收到取消指令我们也不会去执行注册的函数

参数:

  • routine:这个是传输给routine这个函数指针的函数的参数
  • arg:原本设置的取消指令的响应类型,他会存放到这个内存中,可以设置为NULL
  • execute:如果该值为0,则清除出注册函数,而不去执行注册函数的内容,如果该值为非0,则清除出注册函数的同时,去运行注册函数的内容

返回值:

例程:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>

pthread_t tid;//线程ID

//注册的线程退出处理函数
void routine(void *arg)
{
    printf("嘻嘻 arg=%s\n", (char *)arg);
}


void *thread(void *arg)
{
    int i, j, z=5;

    //设置线程不能被pthread_cancel所杀死
//    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);

    //设置线程一旦有人对线程发送取消请求则马上退出线程,不用遇到取消点函数
    pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);

    //在线程被取消的时候,要求在退出线程之前执行routine里面的内容,"哈哈"则是传输给routine的参数
    pthread_cleanup_push(routine, "哈哈");
    //因为sleep是取消点函数,所以采用消耗CPU的方式来延时
    while(z--)
    {
        for( i=100000; i>0; i--)
            for( j=10000; j>0; j--);
        printf("in thread\n");
    }

    //将pthread_cleanup_push所声明如果取消线程时执行的routine函数清除出去,
    //0代表清楚而不执行,非0代表清楚且执行一下pthread_cleanup_push所注册的routine函数里面的内容

    pthread_cleanup_pop(0);

    z=5;
    while(z--)
    {
        for( i=100000; i>0; i--)
            for( j=10000; j>0; j--);

        printf("next thread\n");
    }

    return NULL;
}

void sighand(int signum)
{
    //给指定线程发送取消请求,线程遇到取消点函数才会退出
    pthread_cancel(tid);
}

int main(void)
{
    int retval;
    //设置信号响应函数
    signal(SIGINT, sighand);
    //创建子线程
    pthread_create(&tid, NULL, thread, NULL);
    
    //等待回收子线程
    retval = pthread_join(tid, NULL);
    if(retval != 0)
    {
        fprintf(stderr, "join failed :%s\n", strerror(retval));
        return -1;
    }
    
    printf("join success\n");
    return 0;
}

图片来之——《Linux环境编程图文指南》林世霖

更多推荐