Linux-C编程 / 多线程 / 如何终止某个线程?

示例 demo

最简单的 demo:

static void* thread1_func(void *arg)
{
    int i = 0;

    // able to be cancel
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);

    for(i=0; ; i++) {
        printf("thread1 %d\n", i);
        sleep(1);
    }
}

int main(int argc, char **argv)
{
    pthread_t t;
    void *res;

    pthread_create(&t, NULL, thread1_func, NULL);

    sleep(3);
    pthread_cancel(t);          // cancel thread1
    pthread_join(t, &res);      // wait thread1

    if (res == PTHREAD_CANCELED)
        printf("thread1 was terminate by cancel\n");
    else
        printf("thread1 was not terminate by cancel\n");

    exit(EXIT_SUCCESS);
}

为了突出重点,省略了检查返回值。

运行效果:

thread1 0
thread1 1
thread1 2
thread1 was terminate by cancel

主线程先创建线程 thread1,然后睡眠 3 秒后发出终止 thread1 的请求。

接收到终止请求后,thread1 会在合适的时机被终止掉。

主线程通过 pthread_join() 阻塞等待 thread1 退出。

几个要点

线程终止的 4 种方式:

  1. 线程的执行函数返回了,这和 main() 函数结束类似。

  2. 线程调用了 pthread_exit() 函数,这和调用 exit() 返回类似。

  3. 线程被另一个线程通过 pthread_cancel() 函数取消,这和通过kill() 发送 SIGKILL 信号类似。

  4. 进程终止了,则进程中的所有线程也会终止。

取消某个线程的常规步骤

被取消的线程:

  1. 允许取消,pthread_setcancelstate(),参数可选值:

  • PTHREAD_CANCEL_ENABLE,这是默认值;

  • PTHREAD_CANCEL_DISABLE;

  • 设置取消类型,pthread_setcanceltype(),参数可选值:

    • PTHREAD_CANCEL_ASYNCHRONOUS,异步方式,当发出取消请求后,线程可能会在任何点被杀死。

    • PTHREAD_CANCEL_DEFERRED,延迟方式,线程只会在特定的取消点(cancellation points,调用某个函数前)被杀死。

    发起取消的线程:

    1. 发送取消要求,pthread_cancel(),发出取消请求后,pthread_cancel() 当即返回,不会等待目标线程的退出。

    2. 等待取消完成,pthread_join()。

    哪些函数是取消点?

    POSIX.1 指定了哪些函数一定是取消点:

    点击查看大图

    更多关于取消点的介绍:

    $ man 7 pthreads
      Cancellation points
        ...
            accept()
            aio_suspend()
            clock_nanosleep()
            close()
            ...
    

    阅读开源软件 MJPS-streamer

    MJPG-streamer 是什么?

    简单地说,就是一个开源的流媒体服务器:

    https://github.com/jacksonliam/mjpg-streamer

    通过 mjpg-streamer,你可以通过 PC 浏览器访问到板子上的摄像头图像。

    MJPG-streamer 是如何结束工作线程的?

    MJPG-streamer 运行时一般会有 3 个线程:

    • 主线程;

    • 负责数据的输入的线程 (例如 camera capture thread);

    • 负责输出数据的线程 (例如 http server thread).

    以 http server thread 为例:

    plugins/output_http/httpd.c
    
    void *server_thread(void *arg)
    {
        ...
        pthread_cleanup_push(server_cleanup, pcontext);
        
        // 处理连接
        while(!pglobal->stop) {
            ...
        }
        pthread_cleanup_pop(1);
    }
    

    pthread_cleanup_push() 用于注册清理函数到栈中,当线程遭取消时,会沿该栈自顶向下依次执行清理函数。

    当用户通过按下 ctrl + c 要求结束程序时,主线程会要求杀掉 http server thread 等各种线程:

    static void signal_handler(int sig)
    {
        
        for(i = 0; i < global.outcnt; i++) {
            ...
            pthread_cancel(servers[id].threadID);
            ...
        }
    }
    

    接下来,当 http server thread 遇到某个取消点时,server_cleanup() 会被调用以完成清理工作。

    这里只是简单地分析一下,MJPG-Streamer 里多线程相关的代码挺复杂的,有兴趣的小伙伴们自行阅读吧。

    相关参考

    • 开源软件 / MJPG-Streamer

    • Linux 系统编程(第2版) / 7.7.6 终止线程

    • Linux 程序设计(第4版) / 12.7 取消一个线程

    • Linux-Unix 系统编程手册 / 32 线程:线程取消

    • Unix 环境高级编程 / 11.5 线程终止

    思考技术,也思考人生

    要学习技术,更要学习如何生活

    你和我各有一个苹果,如果我们交换苹果的话,我们还是只有一个苹果。但当你和我各有一个想法,我们交换想法的话,我们就都有两个想法了。

    嵌入式系统 (Linux、RTOS、OpenWrt、Android) 和 开源软件 感兴趣,关注公众号:嵌入式Hacker

    觉得文章对你有价值,不妨点个 在看和赞

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值