C++ 多线程——pthread_cancel 取消线程的疑惑

C++ 多线程——pthread_cancel 取消线程的疑惑

测试环境:Ubuntu18.04

pthread_cancel 简介

pthread_cancel(threadID)会发送终止信号给thread线程,如果成功则返回0,否则为非0值。

pthread_cancel调用并不等待线程终止,它只是向目标线程发Cancel信号, 提出取消请求。

但目标线程如何处理Cancel信号则由目标线程自己决定,或者忽略(当禁止取消时)、或者立即终止(当在取消点或异步模式下)、或者继续运行至Cancelation-point(取消点,下面将描述),总之由不同的Cancelation状态决定。

示例代码

下面这个示例代码将开启两个子线程thread1 thread2

thread1 —> while死循环输出信息

thread2 —> 延时两秒 然后通过pthread_cancel来取消thread1线程

代码如下:

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

void* thread1(void *arg)
{
    while (1)
    {
        std::cout << "thread1 running" << std::endl;
    }
    std::cout << "Leave thread1!" << std::endl;

    return nullptr;
}

void* thread2(void *arg)
{
    sleep(2);
    pthread_cancel(*static_cast<pthread_t*>(arg)); //取消线程
    std::cout << "send pthread_cancel!" << std::endl;

    return nullptr;
}


int main(int argc, char** argv)
{
    // 启动子线程1 join运行
    pthread_t tid1;
    pthread_create(&tid1, nullptr, thread1, nullptr);

    // 启动子线程2 detach运行 计时两秒后 发送pthread_cancel 来结束 子线程1
    pthread_t tid2;
    pthread_create(&tid2, nullptr, thread2, &tid1);

    // pthread_detach()即主线程与子线程分离,两者相互不干涉,不会阻塞主线程,子线程结束同时子线程的资源自动回收
    pthread_detach(tid2);
    
    // pthread_join()即是子线程合入主线程,主线程会一直阻塞,直到子线程执行结束,然后回收子线程资源,并继续向下执行
    pthread_join(tid1, nullptr);

    // 上述代码  由于thread1 会阻塞主线程,所以程序会阻塞在这里无法继续执行。当成功取消thread1后会继续向下执行。这里用打印  "main thread!" 来查看 thread1 是否结束。
    std::cout << "main thread!" << std::endl;
    while (1) {
        sleep(1);
    }

    return 0;
}

运行结果

........
thread1 running
thread1 running
thread1 running
send pthread_cancel!

可以看到程序并没有打印 main thread! 说明 thread1 子线程并没有结束。

难道是因为 线程处于无限循环中,且循环体内没有执行至取消点的必然路径,则线程无法由外部其他线程的取消请求而终止?????

这种问题该怎么解决呢?

解决方法一 手动插入一个取消点

void* thread1(void *arg)
{
    while (1)
    {
        std::cout << "thread1 running" << std::endl;
        
        /*
         * 手动创建一个取消点 pthread_testcancel()
         * pthread_testcancel会检查本线程是否处于Canceld状态,如果是,则进行取消动作,否则直接返回。 
         * 此函数在线程内执行,执行的位置就是线程退出的位置,在执行此函数以前,线程内部的相关资源申请一定要释放掉,他很容易造成内存泄露。
         */
        pthread_testcancel();
    }
    std::cout << "Leave thread1!" << std::endl;

    return nullptr;
}

运行结果

........
thread1 running
thread1 running
thread1 running
send pthread_cancel!
main thread!

可以看到thread1成功取消

解决方法二 sleep 延时

void* thread1(void *arg)
{
    while (1)
    {
        std::cout << "thread1 running" << std::endl;
        
        /*
         * usleep(1)  延时1微妙  也就是0.001毫秒
         */
        usleep(1);
    }
    std::cout << "Leave thread1!" << std::endl;

    return nullptr;
}

运行结果

........
thread1 running
thread1 running
thread1 running
send pthread_cancel!
main thread!

哇哦,也成功了…

难道是while死循环中没有延时 导致cancel信号被忽略??? 还是因为 sleep 延时本身就算是一个取消点???

再改变一下log输出的方式

void* thread1(void *arg)
{
    while (1)
    {
        // std::cout << "thread1 running" << std::endl;
        printf("thread1 running\n");
    }
    std::cout << "Leave thread1!" << std::endl;

    return nullptr;
}

运行结果

........
thread1 running
thread1 running
thread1 running
send pthread_cancel!
main thread!

这到底怎么回事??
只是简单的换了一下log的输出方式,就产生了不一样的结果。
作为小白,我也很疑惑…

pthread 的高级配置

设置本线程对Cancel信号的反应
int pthread_setcancelstate(int state, int *oldstate);
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* thread1(void *arg)
{
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); //允许退出线程
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); //设置立即取消

    while (1)
    {
        std::cout << "thread1 running" << std::endl;
    }
    std::cout << "Leave thread1!" << std::endl;

    return nullptr;
}

运行结果

........
thread1 running
thread1 running
thread1 running
send pthread_cancel!

参考博文

https://www.cnblogs.com/lijunamneg/archive/2013/01/25/2877211.html

https://www.cnblogs.com/wangchaoguo-li/archive/2012/11/08/2760006.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章