线程池的简单实现

在学习使用stl容器的时候,我们见到了stl容器使用了内存池来进行时间优化,因为用户在使用new、malloc等向系统申请空间的时候,身份会发生变化,同时有可能要执行操作系统内的内存处理算法,这可能会比较耗时。于是使用了内存池来提前申请好大块内存空间,需要用户自行进行管理。与内存池类似,提前准备好的线程,用来随时处理任务,这就被称作线程池。线程池是线程的一种使用模式。线程过多会带来调度开销,从而影响缓存局部性和整体性能。而线程池中维护着多个线程,等待分配可并发执行的任务。这就避免了在处理短时间任务时创建和
摘要由CSDN通过智能技术生成

线程池的概念

在学习使用stl容器的时候,我们见到了stl容器使用了内存池来进行时间优化,因为用户在使用new、malloc等向系统申请空间的时候,身份会发生变化,同时有可能要执行操作系统内的内存处理算法,这可能会比较耗时。于是使用了内存池来提前申请好大块内存空间,需要用户自行进行管理。与内存池类似,提前准备好的线程,用来随时处理任务,这就被称作线程池。
线程池是线程的一种使用模式。线程过多会带来调度开销,从而影响缓存局部性和整体性能。而线程池中维护着多个线程,等待分配可并发执行的任务。这就避免了在处理短时间任务时创建和销毁线程的代价。线程池不仅能保证内核的充分利用,还能防止过度调度。可用线程数量取决于可用的并发处理器、处理器内核、内网络sockets等的数量。

线程池的应用场景

  1. 需要大量的线程来完成任务,并且完成任务的时间比较短。WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,任务数量巨大。但是对于长时间的任务,比如一共Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。
  2. 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
  3. 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池的情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,可能会出现错误。

线程池的简单实现

线程池的实现可以先使用一个命名空间来封装这个类。线程池中类的成员变量有:num_代表该线程池中有多少个线程。task_queue_代表一个队列,该成员是一个临界资源,用来存放任务。还有一把锁和一个条件变量。

#include<iostream>
#include<string>
#include<queue>
#include<unistd.h>

namespace ns_threadpool
{
   
    const int g_num = 5;

    template<class T>
    class ThreadPool
    {
   
        private:
        int num_;
        std::queue<T> task_queue_;//该成员是一个临界资源
        pthread_mutex_t mtx_;
        pthread_cond_t cond_;

    };
} // namespace ns_threadpool

线程池中的构造和析构函数:主要是对锁和条件变量进行初始化和销毁操作,并对具体创建线程的数量进行初始化

        ThreadPool(int num = g_num)
            :num_(num)
        {
   
            pthread_mutex_init(&mtx_,nullptr);
            pthread_cond_init(&cond_,nullptr);
        }
        ~ThreadPool()
        {
   
            pthread_mutex_destroy(&mtx_);
            pthread_cond_destroy(&cond_);
        }

初始化线程池:初始化线程池即创建num_数量的线程,这里只需要创建出来即可,不需要等待等操作。需要注意的是这里需要传入this指针,具体原因后面介绍Routine函数时进行介绍

        void InitThreadPool()
        {
   
            pthread_t tid;
            for(int i=0;i<num_;i++)
            {
   
                pthread_create(&tid,nullptr,Routine,(void*)this);
            }
        }

Routine线程执行函数:该函数由于是在类里面,所以如果不加上static来进行修饰,则会自带一个隐藏的this指针,这就和创建线程所需要的函数不符合,因此需要加上static。但是加上static后就不能访问类中的内容,因此之前在创建线程的时候刚好可以将this指针传入。
在该函数的实现方面可以先将线程进行分离再进行后续的处理工作。
为了线程安全还是需要对临界资源的访问进行加锁操作。使用条件变量来控制判断此时是否有任务需要执行,如果没有任务需要执行则等待,有则进行任务处理工作。

        static void* Routine(void* args)
        {
   

            pthread_detach(pthread_self());
            ThreadPool<T>* tp = (ThreadPool<T>*)args;
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在C语言实现一个简单线程池可以按照以下步骤进行: 1. 定义线程池结构体:创建一个结构体,包含线程池中的线程数量、任务队列、互斥锁、条件变量等必要的成员。 ```c typedef struct { pthread_t *threads; // 线程数组 int thread_count; // 线程数量 task_queue_t *task_queue; // 任务队列 pthread_mutex_t mutex; // 互斥锁 pthread_cond_t cond; // 条件变量 int shutdown; // 线程池关闭标志 } thread_pool_t; ``` 2. 定义任务队列结构体:创建一个结构体,用于存储任务队列的信息,包括任务数组、任务数量、头尾指针等成员。 ```c typedef struct { task_t **tasks; // 任务数组 int task_count; // 任务数量 int head; // 队列头指针 int tail; // 队列尾指针 } task_queue_t; ``` 3. 定义任务结构体:创建一个结构体,用于存储具体的任务信息,例如任务函数指针和参数。 ```c typedef struct { void (*function)(void *); // 任务函数指针 void *argument; // 任务参数 } task_t; ``` 4. 初始化线程池:创建线程池,并初始化线程数组、任务队列、互斥锁、条件变量等。 5. 添加任务:将任务添加到任务队列中,并通过条件变量通知线程池中的线程有新任务可执行。 6. 线程函数:线程池中的线程函数,循环从任务队列中取出任务并执行。 7. 等待线程池任务完成:可以通过条件变量等待所有任务执行完毕。 8. 销毁线程池:释放线程池中的资源,包括线程数组、任务队列、互斥锁、条件变量等。 这是一个简单线程池实现的框架,你可以根据自己的需求进行具体的功能扩展和优化。注意在并发环境中使用互斥锁和条件变量来保证线程安全。希望对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值