【Webserver】——线程池的原理,手写线程池

目录

1.什么是线程池?

2.线程池的作用

3.任务队列的设计

4.构造函数

5.push接口设计

6.子线程的执行函数

7. 析构函数

8.测试线程池

9.线程池中的线程数量设定

1.经验值

2.最佳线程数目算法


1.什么是线程池?

 

线程池是一种 利用 池化技术思想 来实现的线程管理技术,主要是为了复用线程、便利地管理线程和任务、并将线程的 创建和任务的执行 解耦开来。我们可以创建线程池来复用已经创建的线程来降低频繁创建和销毁线程所带来的资源消耗。

 

2.线程池的作用

  • 复用线程资源;
  • 减少线程创建和销毁的开销
  • 可异步处理生产者线程的任务;
  • 减少了多个任务(不是一个任务)的执行时间
  • 线程资源开销与cpu资源的平衡选择

3.任务队列的设计

线程池的任务队列可以数组,链表,队列,栈等容器来进行设计

我将任务队列 提前开辟一块连续的空间 作为 环形任务队列,不用频繁的去分配内存,减少内存碎片。

 

//任务队列
//T是任务
template <typename T>
struct task_queue_t{
    task_queue_t(int _count=QUEUECOUNT)
    {
        if(_count<=0){
            throw "queue is zero";
        }

        max_count=_count;
        cur_count=0;
        queue=new T[max_count];
        if(queue==nullptr)
        {
            throw bad_alloc();
        }
        head=0;
        tail=0;
    }

    ~task_queue_t()
    {
        if(queue!=nullptr){
            delete queue;
            queue=nullptr;
        }
    }

    size_t head;  //可读任务的位置
    size_t tail;  //可写任务的位置
    size_t max_count;//最大的任务数量
    size_t cur_count;//当前队列中的任务数量
    T* queue;
};

4.构造函数

 

template <typename T>
threadpool<T>::threadpool(size_t _thrd_count,size_t _task_size)
{
    pool=new pthread_t[thrd_count];
    if(pool==nullptr)
    started=0;
    task_queue=new task_queue_t<T*>(_task_size);
    if(task_queue==nullptr)
    {
        throw bad_alloc();
    }

    pthread_mutex_init(&lock,NULL);
    pthread_cond_init(&cond,NULL);

    for(int i=0;i<_thrd_count;i++)
    {
        pthread_create(&pool[i],NULL,routine,this);
        thrd_count++;
        started++;
    }
}

 

5.push接口设计

 

template <typename T>
int threadpool<T>::push(T* task){
    if(pool==nullptr||task_queue==nullptr){
        return -1;
    }

    if(pthread_mutex_lock(&lock)==-1){
        return -1;
    }

    if(close){
        pthread_mutex_unlock(&lock);
        return -1;
    }

    if(task_queue->cur_count==task_queue->max_count){
        pthread_mutex_unlock(&lock);
        return -1;
    }

    task_queue->queue[task_queue->tail]=task;
    if(pthread_cond_signal(&cond)!=0){
        pthread_mutex_unlock(&lock);
        return -1;
    }
    
    task_queue->tail=(task_queue->tail+1)%task_queue->max_count;
    task_queue->cur_count++;
    pthread_mutex_unlock(&lock);
    return 0;
}

6.子线程的执行函数


template <typename T>
void* threadpool<T>::routine(void* arg)
{
    threadpool<T>* pool=(threadpool<T>*) arg;

    while(true)
    {
        /*
        1.加锁,进入请求池获取任务
        2.如果没有任务,则阻塞在条件变量中,解锁
        3.如果有任务,取出任务则运行该任务
        */

       pthread_mutex_lock(&pool->lock);

       while(pool->task_queue->cur_count==0&&pool->close==false)
       {
          pthread_cond_wait(&pool->cond,&pool->lock);
       }
       
       if(pool->close==true)break;
       task_queue_t<T*>* queue=pool->task_queue;
       T* t=queue->queue[queue->head];
       pool->task_queue->head=(pool->task_queue->head+1)%pool->task_queue->max_count;
       pool->task_queue->cur_count-=1;//任务数量-1
       pthread_mutex_unlock(&pool->lock);
       t->process();
    }
    //退出线程
    pool->started--;//活跃线程减1
    pthread_mutex_unlock(&pool->lock);
    pthread_exit(NULL);
    return NULL;
}

7. 析构函数

template <typename T>
threadpool<T>::~threadpool()
{
    /*
    1.先请求池中的任务全部处理完
    2.在退出所有的线程
    3.释放线程池资源
    */

   if(pool==nullptr){
    return;
   }
  if(pthread_mutex_lock(&lock)<0){
    //加锁失败
     m_errno=2;
     return;
   }
   if(close==true)  
   {
     thread_pool_free();
     return;
   }

   close=true;
   if(pthread_cond_broadcast(&cond)<0||pthread_mutex_unlock(&lock)<0){
      thread_pool_free();
      m_errno=3;
      return;
   }

   wait_all_done();
   thread_pool_free();
   return;
}

template <typename T>
int threadpool<T>::wait_all_done()
{
    int ret=0;
    if(pool==nullptr){
        return 1;
    }

    for(int i=0;i<thrd_count;i++)
    {
        if(pthread_join(pool[i],NULL)!=0){
            ret=1;
        }
    }
    return ret;
}


template <typename T>
int threadpool<T>::thread_pool_free()
{

    if(pool!=nullptr||started>0){
        //还有活跃线程,不能还不能销毁资源
        return -1;
    }

    delete[] pool;
    pool=nullptr;

    if(task_queue!=nullptr)
    {
      delete task_queue;
      task_queue=nullptr;
    }

    pthread_mutex_destroy(&lock);
    pthread_cond_destroy(&cond);

    started=0;
    thrd_count=0;
}

为什么要需要先等子线程退出后,才释放线程池资源?

为了保证线程池中任务能够全部被执行完全,如果线程池还有线程在执行,如果主线程突然退出,会导致一些任务没有被执行,或者执行的不完全。

8.测试线程池

下面这段代码测试线程池中的线程是否可以并发执行任务。

  
struct task{
    static  int done ;
    static  pthread_mutex_t lock;
    public:
    void process(){
     usleep(10000);
      pthread_mutex_lock(&lock);
      done++;
      printf("%d:doing %d task\n",pthread_self(), done);
      pthread_mutex_unlock(&lock);
    }
};
int task::done = 0;
pthread_mutex_t task::lock;

int main()
{
    threadpool<task>* pool=new threadpool<task>();
    if (pool == NULL) {
        printf("thread pool create error!\n");
        return 1;
    }

    task* t=new task;
    while (pool->push(t)==0) {
       /// pthread_mutex_lock(&lock);
        nums++;
      //  pthread_mutex_unlock(&lock);
        t=new task;
    }

    printf("add %d tasks\n", nums);
    
    pool->wait_all_done();

   // printf("did %d tasks\n", done);
}

9.线程池中的线程数量设定

线程池中的线程数量设定 是需要 与cpu资源进行平衡选择

线程中的线程数量不是创建越多越好,因为一个cpu只能运行一个线程,创建线程池的目的是需要充分利用cpu资源。

怎样重复利用cpu资源?

当有些线程进行io阻塞等待的时候,此时是不会利用cpu资源的,这时候就可以切换到另一个线程,使cpu不断的进行运算。

1.经验值

配置线程数量之前,首先要看任务的类型是 IO密集型,还是CPU密集型

什么是IO密集型?
比如:频繁读取磁盘上的数据,或者需要通过网络远程调用接口。
什么是CPU密集型?
比如:非常复杂的调用,循环次数很多,或者递归调用层次很深等。

如果是 混合型(既包含IO密集型,又包含CPU密集型)的如何配置线程数?

混合型如果 IO密集型,和CPU密集型 的执行时间相差不太大,可以拆分开,以便于更好配置。如果执行时间相差太大,优化的意义不大,比如IO密集型耗时60s,CPU密集型耗时1s。

2.最佳线程数目算法

除了上面介绍是经验值之外,其实还提供了计算公式:

最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目

因为很显然,线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程。

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]和\[2\]提供了关于线程池的实现细节,包括创建多个线程、向工作队列中添加任务、线程池的启动和销毁等。线程池是一种用于管理和复用线程的技术,可以提高多线程程序的性能和效率。 引用\[3\]提供了关于同步与异步、阻塞与非阻塞的解释。同步和异步强调的是消息通信机制,阻塞和非阻塞强调的是程序在等待调用结果时的状态。在webserver中,线程池可以采用同步I/O模拟proactor模式,主线程负责监听事件,工作线程负责处理客户请求。 综上所述,webserver线程池是一种通过管理和复用线程来提高性能和效率的技术。它可以实现同步或异步的消息通信机制,并可以采用阻塞或非阻塞的方式处理请求。 #### 引用[.reference_title] - *1* *3* [webserver-线程池的同步机制类以及线程池实现](https://blog.csdn.net/weixin_44654302/article/details/128093190)[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* [手把手实现webserver网页服务器(二)-- 线程池的实现](https://blog.csdn.net/twopq/article/details/122428412)[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、付费专栏及课程。

余额充值