Linux线程池实现之改进

9 篇文章 0 订阅

上一篇写到实现了一个线程池,但是那个线程池的任务回调函数却用到了四个变量之多,自己又仔细思考了一下,这样做只是为了把任务函数的参数传进行去而以,这样做真的是多此一举,既然参数据设定为void *类型的,那就完全可以传进行所有类型的参数,完全可以将参数组成一个结构体, 将结构体的指针传给回调函数。

改进代码如下:


ThreadWorker结构体改进

typedef struct worker{
    void (*process)(void * arg);
    void * arg;
    struct worker *next;
} ThreadWorker;

AddWorker方法改进,只有一个参数:


void ThreadPool::AddWorker(void (*process)(void * ), void * arg)
{
    ThreadWorker * new_worker = (ThreadWorker *)malloc(sizeof(ThreadWorker));
    new_worker->process = process;
    new_worker->arg = arg;
    new_worker->next = NULL;

    pthread_mutex_lock(&m_queueLock);
    ThreadWorker * wk = m_workerHead;
    if (wk == NULL)
    {
        m_workerHead = new_worker;
    }else
    {
        while(wk->next != NULL)
            wk = wk->next;
        wk->next = new_worker;
    }

    assert(m_workerHead != NULL);

    m_workerSize++;

    pthread_mutex_unlock(&m_queueLock);

    pthread_cond_signal(&m_queueReady);
    return;

}


thread_loop函数中的改进:


void * ThreadPool::__thread_loop(void * arg)
{
        
        ......
        
        (*(worker->process))(worker->arg);
        free(worker);
        worker = NULL;
    }
    return NULL;
}




调用示例:

#include <diff_area/LoadData.h>
#include <diff_area/Diff.h>
#include <thread_pool/ThreadPool.h>
#include <unistd.h>

void do_one_mesh(char * base, char * updated, char *input_name, char * output_path);

void do_in_thread_pool(void * args);

//参数结构体
typedef struct {
    char * output_path;
    const char * mesh_name;
    Regions base_regions;
    Regions updated_regions;
} ThreadWorkerArgs;

int main(int argc, char ** argv)
{
    char * root_path = NULL;
    char * updated = NULL;
    char * base = NULL;
    char * input_name = NULL;
    char * output_path = NULL;
    bool one_mesh = false;
    int thread_num = 1;
    int c;
    while((c = getopt(argc, argv, "j::o::n::r::b::u::Oh")) != -1)
    {
        switch(c)
        {
            case 'j':
                thread_num = atoi(optarg);
                break;
            case 'r':
                root_path = optarg;
                break;
            case 'n':
                input_name  = optarg;
                break;
            case 'O':
                one_mesh = true;
                break;
            case 'b':
                base = optarg;
                break;
            case 'u':
                updated = optarg;
                break;
            case 'o':
                output_path = optarg;
                break;
            case 'h':
                printf("参数说明:\n");
                printf("-O后面不需要带参数,其他均需参数\n");
                printf("参数一定要紧跟选项后面\n");
                printf("例如:-b/home/data\n");
                printf("-n: 数据文件名\n");
                printf("-b: 基板版本号,如果带-O选项,那么就是基板数据所在的目录\n");
                printf("-u: 差分版版本号,如果带-O选项,那么就是差分版数据所在的目录\n");
                printf("-r: 数据根目录,当带-O选项是,该选项无用\n");
                printf("-O: 带此选项为直接指定差分版与基板数据路径\n");
                printf("-o: 需带参数,为结果输出目录\n");
                return 0;
            case '?':
                fprintf(stderr, "please use [-O] [-n] [-b] [-u] -[i] options\n");
                return -1;
                break;
        }
    }
    if (updated == NULL || base == NULL || input_name == NULL || output_path == NULL)
    {
        fprintf(stderr, "-n, -b, -o options can not be empty!\n");
        return -1;
    }
    if (one_mesh)
    {
        do_one_mesh(base, updated, input_name, output_path);
    }else
    {
        int base_version = 0;
        int updated_version = 0;
        if (root_path == NULL)
        {
            fprintf(stderr, "-b options can not be empty!\n");
            return -1;
        }
        if ((base_version = atoi(base)) == 0)
        {
            fprintf(stderr, "-b options must be an integer!\n");
            return -1;
        }
        if ((updated_version = atoi(updated)) == 0)
        {
            fprintf(stderr, "-u options must be an integer!\n");
            return -1;
        }
        std::map<std::string, std::string> base_paths, updated_paths;
        AutoGetPreVersion(root_path, base_version, updated_version, base_paths, updated_paths);
        MeshRegions base_mesh_regions, updated_mesh_regions;
        LoadAll(input_name, base_paths, updated_paths, base_mesh_regions, updated_mesh_regions);

        int mesh_num = updated_mesh_regions.size(); 

        ThreadPool * pool = new ThreadPool(thread_num);
        pool->ThreadPoolInit();

        BOOST_FOREACH(MeshRegionsPair & pair, updated_mesh_regions)
        {
            ThreadWorkerArgs *arg = new ThreadWorkerArgs();
            arg->mesh_name = pair.first.c_str();
            arg->output_path = output_path;
            arg->base_regions = base_mesh_regions[pair.first];
            arg->updated_regions = pair.second;
            pool->AddWorker(do_in_thread_pool, arg);
        }
        while (pool->GetWorkerNum() != 0)
        {
            double process = (mesh_num - pool->GetWorkerNum())/mesh_num * 100;
            //printf("\rprocessing: %f%%", process);
            sleep(1);
        }
        pool->ThreadPoolDestroy();
        //printf("\rprocessing: 100%%\n");

        return 0;
    }
    return 0;
}

void do_one_mesh(char * base, char * updated, char * input_name, char * output_path)
{
    Regions base_regions;
    Regions updated_regions;
    Regions del;
    Regions add;
    LoadOne(base, updated, input_name, base_regions, updated_regions);
    DoOneDiff(base_regions,  updated_regions, add, del);
    WriteOne(output_path, add, del);
}
//线程池任务回调函数,注意参数结构体的作用
void do_in_thread_pool(void * args)
{
    ThreadWorkerArgs * wk_args = (ThreadWorkerArgs *)args;

    const char * mesh_name = wk_args->mesh_name;
    char * output_path = wk_args->output_path;
    Regions base_regions = wk_args->base_regions;
    Regions updated_regions = wk_args->updated_regions;

    Regions add;
    Regions del;
    printf("now doing mesh: %s\n", mesh_name);
    DoOneDiff(base_regions, updated_regions, add, del);
    printf("now writing result\n");
    WriteByMesh(output_path, mesh_name, add, del);
    printf("mesh: %s has been done\n", mesh_name);
}


Linux线程池的原理是通过任务队列和线程队列的配合来实现的。任务队列用于存储需要处理的任务,而线程队列则用于存储可用的线程。当有任务需要处理时,线程池会从线程队列中获取一个空闲线程来执行任务。当任务执行完毕后,线程会返回线程队列,等待下一个任务的到来。 线程池的初始化过程包括申请内存、初始化锁和信号量、设置最大线程数量、最大空闲线程数量等。在初始化完成后,线程池就可以开始接收任务并进行处理了。 在任务执行函数中,线程会从任务队列中获取任务,并执行相应的操作。当任务执行完毕后,线程会继续等待下一个任务的到来。需要注意的是,在线程退出后,线程节点的内存并没有归还给操作系统,这是因为设计相对简单,可以根据需要进行改进。 总结来说,Linux线程池的原理是通过任务队列和线程队列的配合来实现任务的分配和执行。这种设计可以提高程序的效率和性能,同时也可以避免频繁地创建和销毁线程。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* [linux下c语言版线程池](https://blog.csdn.net/weixin_42398658/article/details/123849826)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Linux线程池的原理及实现](https://blog.csdn.net/weixin_44344462/article/details/96432009)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值