干翻全岛蛙蛙 发表于 2024-7-22 22:24:11

【linux 多线程并发】线程退出主动清理函数的利用,释放线程申请的资源,异

线程退出回调函数

   ​专栏内容:


[*] 参天引擎内核架构
本专栏一起来聊聊参天引擎内核架构,以及怎样实现多机的数据库节点的多读多写,与传统主备,MPP的区别,技能难点的分析,数据元数据同步,多主节点的情况下对故障容灾的支持。
[*] 手写数据库toadb
本专栏主要先容怎样从零开发,开发的步骤,以及开发过程中的涉及的原理,遇到的问题等,让各人能跟上并且可以一起开发,让每个必要的人成为到场者。
本专栏会定期更新,对应的代码也会定期更新,每个阶段的代码会打上tag,方便阶段学习。
    ​开源贡献:


[*]toadb开源库
个人主页:我的主页
管理社区:开源数据库
座右铭:天行健,君子以自强不息;阵势坤,君子以厚德载物.


媒介

当代的CPU都是多core处理器,而且在intel处理器中每个core又可以多个processor,形成了多任务并行处理的硬件架构,在服务器端的处理器上架构又有一些不同,传统的接纳SMP,也就是对称的多任务处理架构,每个任务都可以对等的访问所有内存,外设等,而现在在ARM系列CPU上,多接纳NUMA架构,它将CPU核分了几个组,给每个组的CPU core分配了对应的内存和外设,CPU访问对应的内存和外设时速率最优,跨组访问时性能会降底一些。
随着硬件技能的持续发展,它们对一样平常应用的性能优化本领越来越强,同时对于服务器软件的开发,提出更高要求,要想达到极高的并发和性能,就必要充分利用当前硬件架构的特点,对它们举行压榨。那么,我们的应用至少也是要接纳多任务架构,不管是多线程还是多进程的多任务架构,才可以充分利用硬件的资源,达到高效的处理本领。
当然多任务框架的接纳,不仅仅是多线程的执行,必要对多任务下带来的问题举行处理,如任务执行返回值获取,任务间数据的通报,任务执行次序的协调;当然也不是任务越多处理越快,要制止线程过多导致操纵系统夯住,也要防止任务空转过快导致CPU利用率飙高。
本专栏主要先容利用多线程与多进程模型,怎样搭建多任务的应用框架,同时对多任务下的数据通信,数据同步,任务控制,以及CPU core与任务绑定等干系知识的分享,让各人在现实开发中轻松构建自已的多任务程序。
概述

在线程运行的过程中,会有一些线程私有的数据,在退出时必要线程自己来清理,或者在线程退出时,必要通知其它线程。在现实线程运行时,对于正常预期的退出,我们可以添加代码举行处理,但是对于线程非常处理部分,有些非常必要线程立即退出,这些地方我们很难预期。
怎样在线程退出时,主动调用我们界说的清理函数呢?
线程库的计划者已经早早就思量到了这一点,计划了退出清理回调函数的栈,线程退出时按压入栈的次序,依次从栈顶弹出回调函数执行。下面我们来看它们的具体利用方法。
接口说明

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


[*]参数说明
routine, 用户界说的清理函数;
arg, 用户界说的清理函数的入参;
execute, 是否要执行弹出的清理函数;取值为0时,不执行;大于0,执行;
利用说明

清理回调机制的利用分为两个阶段;


[*]装载清理函数
在线程开始阶段,将界说的清理函数压入栈,当有多个清理函数时,压栈的次序必要用户自界说,只管与资源申请的次序保持划一,制止清理次序错位;
必要留意的是,该步骤最幸亏没有错误发生,或者资源申请之前。


[*]清理函数执行
在线程处理完成后,对清理函数弹出栈,在弹出栈的同时,还可以选择是否执行相应的清理函数;如果是正常执行,可能已经释放了干系资源。
当非常发生时,线程会提前退出,此时用户没有时机操纵清理函数栈,也没有时机选择清理函数是否必要执行;在这种清况下,线程库会主动从栈中依次弹出清理函数,并且默认都会执行。
下面我们用两个线程来模仿正常退出和非常提前退出的情况。
示例代码

/*
* created by senllang 2023/12/24
* mail : study@senllang.onaliyun.com
* Copyright (C) 2023-2023, senllang
*/
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define THREAD_NAME_LEN 32
typedef struct ThreadData
{
        char threadName;
        unsigned long threadId;
}ThreadData;

__thread ThreadData *pthreadData = NULL;

void threadCleanUp(void* arg)
{
        char *str = (char *)arg;
       
        if(pthreadData != NULL)
        {
                printf("[%lu][%s]cleanup process (%s)\n", pthreadData->threadId, pthreadData->threadName, str);
                free(pthreadData);
                pthreadData = NULL;
        }
        else
        {
                printf("[%lu] no work will be doing (%s). \n", pthread_self(), str);
        }

        return;
}

void* thr1Func(void* arg)
{
    char *parg = (char *)arg;

        pthreadData = (ThreadData *)malloc(sizeof(ThreadData));
        memset(pthreadData, sizeof(ThreadData), 0x00);

        strncpy(pthreadData->threadName, parg, THREAD_NAME_LEN);
        pthreadData->threadId = pthread_self();

        pthread_cleanup_push(threadCleanUp, (char*)"first handler");
        pthread_cleanup_push(threadCleanUp, (char*)"second handler");
    printf("[%lu][%s]cleanup process push completed. \n", pthreadData->threadId, pthreadData->threadName);

        /* cleanup process will not be running. */
        pthread_cleanup_pop(1);
        pthread_cleanup_pop(1);

        return NULL;
}

void* thr2Func(void* arg)
{
        int ret = 2;
    char *parg = (char *)arg;

        pthreadData = (ThreadData *)malloc(sizeof(ThreadData));
        memset(pthreadData, sizeof(ThreadData), 0x00);

        strncpy(pthreadData->threadName, parg, THREAD_NAME_LEN);
        pthreadData->threadId = pthread_self();

        pthread_cleanup_push(threadCleanUp, (char*)"first handler");
        pthread_cleanup_push(threadCleanUp, (char*)"second handler");

    printf("[%lu][%s]cleanup process push completed. \n", pthreadData->threadId, pthreadData->threadName);

        /* make a error, exiting */
        if(ret == 2)
                pthread_exit(&ret);

        pthread_cleanup_pop(0);
        pthread_cleanup_pop(0);
}


int main(int argc, char* argv[])
{
    int err;
        pthread_t        tid1, tid2, tid3;
        void        *ret;

        err = pthread_create(&tid1, NULL, thr1Func, "thread 1");
        if(0 != err)
                perror("can't create thread 1");

        err = pthread_create(&tid2, NULL, thr2Func, "thread 2");
        if(0 != err)
                perror("can't create thread 2");


        pthread_join(tid1, &ret);
        pthread_join(tid2, &ret);

        return 0;
}

统共创建两个线程,每个线程都有一个本地数据的指针,在线程运行时动态申请内存;为每个线程压入了两个清理函数;


[*]线程一正常退出,选择执行清理函数
[*]线程二模仿出错时,非常提前退出,而在正常退出时,我们选择不执行清理函数;
执行结果

$ make
gcc-I./ -DTEST_PRO -lpthread -g -c cleanupThreads.c
gcc-I./ -DTEST_PRO -lpthread -g cleanupThreads.o -o hatch-0-01
$ ./hatch-0-01
cleanup process push completed.
cleanup process push completed.
cleanup process (second handler)
no work will be doing (first handler).
cleanup process (second handler)
no work will be doing (first handler).
从结果可以看到,线程一退出时,两个清理函数都执行了。
线程二非常退出时,两个清理函数也都执行了,虽然我们在退出时选择不执行清理函数,但是系统在非常时主动弹出并默认执行了所有清理函数;
总结

线程退出时的清理函数栈,在现实应用程序代码计划时,会让代码运行的更加健壮,让多线程下,岂论是非常退出还是正常退出,都有时机来对线程中用户资源的清理。
本文所涉及的代码已经上传到工程hatchCode, 在multipleThreads/example_05目录下,请各人点star,后续文章的代码也会上传到此工程。
结尾

   非常感谢各人的支持,在欣赏的同时别忘了留下您宝贵的评论,如果觉得值得鼓励,请点赞,收藏,我会更加努力!
作者邮箱:study@senllang.onaliyun.com
如有错误或者疏漏欢迎指出,相互学习。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 【linux 多线程并发】线程退出主动清理函数的利用,释放线程申请的资源,异