线程对pthread_cancel()响应

文章介绍了如何使用pthread_cancel、pthread_setcancelstate、pthread_setcanceltype控制线程对终止信号的响应,以及pthread_cleanup_push和pthread_cleanup_pop用于设置线程终止前的清理程序。实验展示了不同响应策略对线程行为的影响。
摘要由CSDN通过智能技术生成

一个线程可以用pthread_cancel()来向一个指定的线程发送终止信号,接受终止信号的线程可以设置对此信号的响应:
(1)用pthread_setcancelstate()来决定是否响应该信号;
(2)用pthread_setcanceltype()来决定响应信号的方式(延迟响应/立即响应);
(3)用pthread_cleanup_push()和pthread_cleanup_pop()设置线程终止前的退出程序

设置线程是否响应该信号

线程默认能响应pthread_cancel发来的信号,将线程设置为不响应该信号的话,用下面这个函数:

#include <pthread.h>
int pthread_setcancelstate(int state, int *oldstate);
函数功能:设置线程是否响应pthread_cancel发来的信号
参数:
state-----> PTHREAD_CANCEL_ENABLE(能响应)/ PTHREAD_CANCEL_DISABLE(不响应)
oldstate----->设置为NULL的话,就不能保留线程终止前的状态
返回值:成功返回0;失败返回非0值。

test: 将线程设置为不响应该信号一段时间,再将线程设置为能响应该信号,查看结果

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

//int pthread_setcancelstate(int state, int *oldstate);

void *thread_func(void *arg)
{
    //将线程设置为不相应pthread_cancel发来的信号
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);

    for(int i = 0; i < 5; ++i)
    {
        printf("%d rose\n", i + 1);
        sleep(1);

        //当打印了三次rose后,将该线程设置为响应pthread_cancel发来的终止信号
        if(2 == i)
           pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); 
    }

    pthread_exit(NULL);
}

int main(int argc, char const *argv[])
{
    pthread_t tid;
    pthread_create(&tid, NULL, thread_func, NULL);
    
    pthread_cancel(tid);    //向线程发送终止信号

    pthread_join(tid, NULL);    

    return 0;
}

在这里插入图片描述
结果分析:子线程收到了由主线程发来的终止信号,但是由于子线程被设置为了(PTHREAD_CANCEL_DISABLE)不响应终止信号,在for循环里打印3次rose后,子线程被设置为了能响应终止信号,那么子进程应该在此时终止,但是为什么打印了第4次rose呢?是因为线程默认是延迟响应pthread_cancel发来的信号,延迟的意思是:当线程接收到终止信号后,会继续执行,直到遇到一个取消点函数(在这个例子里,printf()就是一个取消点函数),执行这个取消点函数,才终止该线程。

设置线程响应信号的方式

int pthread_setcanceltype(int type, int *oldtype);
函数功能:设置线程对pthread_cancel发来的信号所做的响应方式
参数:
type----->PTHREAD_CANCEL_DEFERRED(取消请求被延迟,直到线程下一次调用作为取消点的函数,线程的默认响应方式就是延迟响应)/PTHREAD_CANCEL_ASYNCHRONOUS(立即响应终止信号)
oldtype----->保存之前的类型状态,不保存的话,设置为NULL
返回值:成功返回0;失败返回非0错误码。
取消点函数:fprintf()、fputc()、fputs()、sleep()、printf()、usleep()

在上一次测试中验证了线程默认是延迟响应终止信号;
test: 将线程设置为立即响应终止信号;

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

//int pthread_setcancelstate(int state, int *oldstate);

void *thread_func(void *arg)
{
    //将线程设置为立即响应终止信号
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);

    //将线程设置为不相应pthread_cancel发来的信号
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);

    for(int i = 0; i < 5; ++i)
    {
        printf("%d rose\n", i + 1);
        sleep(1);

        //当打印了三次rose后,将该线程设置为响应pthread_cancel发来的终止信号
        if(2 == i)
           pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); 
    }

    pthread_exit(NULL);
}

int main(int argc, char const *argv[])
{
    pthread_t tid;
    pthread_create(&tid, NULL, thread_func, NULL);

    pthread_cancel(tid);    //向线程发送终止信号

    pthread_join(tid, NULL);    

    return 0;
}

在这里插入图片描述
结果分析:将线程设置为立即响应终止信号后,这次打印了三次rose,即再for循环里当线程被设置为响应终止信号后,立马就会执行终止信号,而不会延迟到下一个取消点函数后再去执行终止信号。

设置线程执行终止信号前的退出程序

#include <pthread.h>
void pthread_cleanup_push(void (*routine)(void *),void *arg);
函数功能:线程收到pthread_cancel信号终止前执行退出函数,这些函数被叫做例程函数,可以设置多个例程函数,多个函数被创建为例程函数的话,这些函数是以入栈的形式被设置
参数:
routine----->线程被终止前执行的函数
arg----->传递给routine函数的参数

void pthread_cleanup_pop(int execute);
函数功能:以出栈的形式按照先进后出依次执行例程函数
参数:
execute----->设置为0的话,不会执行例程函数;设置为非0值时,会先执行例程函数,再执行终止信号

这两个函数的特点:
(1)如果线程收到pthread_cancel()发来的终止信号,会立即执行例程函数,不会往下执行;
(2)如果线程没有收到pthread_cancel()发来的终止信号,当执行到pthread_cleanup_pop()函数时,会根据此函数的参数来决定是否执行例程函数;
(3)pthread_cleanup_push()和pthread_cleanup_pop()必须成对调用,因为这两个函数的实现使用宏定义来实现的,而且是嵌套的宏定义,如果个数不对等,编译器会给出关于大括号 { 的错误提示。
在这里插入图片描述

test:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <pthread.h>

// void pthread_cleanup_push(void (*routine)(void *),void *arg);
// void pthread_cleanup_pop(int execute);

//例程函数
void func1(void *arg)
{
    printf("例程函数func1\n");
}
void func2(void *arg)
{
    printf("例程函数func2\n");
}
void func3(void *arg)
{
    printf("例程函数func3\n");
}

//线程函数
void* thread_func(void *arg)
{
    //将3个历程函数压栈
    pthread_cleanup_push(func1, NULL);
    pthread_cleanup_push(func2, NULL);
    pthread_cleanup_push(func3, NULL);

    for(int i = 0; i < 5; ++i)
    {
        printf("线程函数thread_func\n");
        sleep(1);
    }
    
    //将3个例程函数出栈
    pthread_cleanup_pop(1);
    pthread_cleanup_pop(1);
    pthread_cleanup_pop(1);

    pthread_exit(NULL);
}


int main(int argc, char const *argv[])
{
    pthread_t tid;
    if(pthread_create(&tid, NULL, thread_func, NULL) != 0)
    {
        perror("pthread_create");
        return -1;
    }

    sleep(2);   //主线程休眠2秒
    pthread_cancel(tid);    //向创建的线程发送终止信号

    pthread_join(tid, NULL);
    
    return 0;
}

在这里插入图片描述

结果分析:在主线程给子线程发送终止信号后,子线程会以出栈的形式去执行例程函数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值