C++实现简单的线程池

线程池编程简介:

在我们的服务端的程序中运用了大量关于池的概念,线程池、连接池、内存池、对象池等等。使用池的概念后可以高效利用服务器端的资源,比如没有大量的线程在系统中进行上下文的切换,一个数据库连接池,也只需要维护一定里的连接,而不是占用很多数据库连接资源。同时它们也避免了一些耗时的操作,比如创建一个线程,申请一个数据库连接,而且可能就只使用那么一次,然后就立刻释放刚申请的资源,效率很低。

在我的上一篇blog中已经实现一个线程基类了,在这里我们只需要实现一个线程池类ThreadPool和该线程池调度的工作线程类WorkThread即可,而且WorkThread是继承自Thread类的。

实现思路:

一个简单的线程池的实现思路一般如下:

  1. 在ThreadPool中创建多个线程(WorkThreadk对象),每个线程均处于阻塞状态,等待任务的到来
  1. ThreadPool提供一个提交任务的接口,如post_job(ProcCallback
    func, void* data); post_job后会立即返回,不会阻塞
  2. ThreadPool维护一个空闲线程队列,当客户程序调用post_job()后,如果空闲队列中有空闲线程,则取出一个线程句柄,并设置任务再给出新任务通知事件即可,处理等待的线程捕捉到事件信号后便开始执行任务,执行完后将该线程句柄重新push到空闲线程队列中
  3. 该线程池采用回调函数方式

首先我们实现一个WorkThread类:

         cb_func_    = NULL;
         param_        = NULL;
     }
               {
         cb_func_    = func;
         param_        = param;
         notify();                        {
                      cb_func_(param_);
     
                   param_        = NULL;
     
               }
     
      ThreadPool*    thr_pool_;           };

该WorkThread中,有一个回调函数指针和参数,当有新任务时,会在run()中被调用,执行完后会将该线程移动到空闲线程队列,等待下一次任务的提交。

ThreadPool类定义如下:

        WorkThread*    get_idle_thread();                                       size_t                thr_num_;                                     ThreadPool&     };
      线程池实现的关键是如何创建多个线程,并且当任务来临时可以从线程池中取一个线程(也就是去得到其中一个线程的指针),然后提交任务并执行。还有一点就是当任务执行完后,应该将该线程句柄重新加入到空闲线程队列,所以我们将ThreadPool的指针传入给了WorkThread,thr_pool_最后可以调用move_to_idle_que(this)来将该线程句柄移到空闲队列中。
ThreadPool中一些关键代码的实现:
                  {
             printf("start_thread_pool: failed when create a work thread: %d\n", i);
             delete pthr;
                      }
         append_idle_thread(pthr);        
     }
      }
  {
          {
         WorkThread* pthr = thr_vec_[i];
         pthr->join();
         delete pthr;
     }
     thr_vec_.clear();
     idle_que_.clear();
      }
  {
     stop_thread_pool();
 }
  {
     thr_vec_.push_back(pthread);
     idle_que_.push(pthread);
 }
  {
     idle_que_.push(idlethread);
 }
 WorkThread* ThreadPool::get_idle_thread()
 {
     WorkThread*    pthr = NULL;
              pthr = idle_que_.take();
      }
  {
     assert(func != NULL);
     WorkThread* pthr = get_idle_thread();
          {
         apr_sleep(500000);
         pthr = get_idle_thread();
     }
     pthr->set_job(func, data);
 }
ThreadPool中的BlockQueue<WorkThread*> 也就是一个线程安全的队列,即对std::deque做了一个包装,在插入和取出元素时加了一个读写锁。
使用示例:
//任务执行函数,必须是ProcCallback类型
void count(void* param)
{
// do some your work, like: 
int* pi = static_cast<int*>(param);
int val = *pi + 1;
printf("val=%d\n", val);
pelete pi;
}
//程序中使用如下:
ThreadPool* ptp = new ThreadPool();
ptp->start_thread_pool(3);
//启动3 个线程
ptp->post_job(count, new int(1));
//提交任务
ptp->post_job(count, new int(2));
ptp->post_job(count, new int(3));
//程序线束时
ptp->stop_thread_pool();
其实count()函数就是我们的业务实现代码,有任务时,可以提交给线程池去执行。
结尾:
    其实实现一个线程池或其它什么池并不难,当然线程安全和效率还是要从多写代码的经验中获取。像这个线程池也就是基于预创多个建线程,保保存好它们的线程句柄,当有新任务时取一个线程执行即可,执行完后一定要归还到空闲线程队列中,当然我们可以在线程池中增加一个任务队列,因为当post_job()时,若当时没有空闲线程,有两种方案,一是等待有空闲线程,二是加入到任务队列,当WorkThread线程执行完一个任务后,从任务队列中取一个任务继续执行即可,不会阻塞在post_job()中。
  另外,我们可以封装一些线程安全的队列和map什么的,这样在程序中就不用担心创建一个多线程共享的队列时,还必须创建一个锁,挺麻烦的,比如上面的BlockQueue<Type>直接拿来用就行了。

【C/C++开发】C++实现简单的线程池的更多相关文章

  1. 简单C++线程池

    简单C++线程池 Java 中有一个很方便的 ThreadPoolExecutor,可以用做线程池.想找一下 C++ 的类似设施,尤其是能方便理解底层原理可上手的.网上找到的 demo,基本都是介绍的 ...

  2. linux网络编程之简单的线程池实现

    转眼间离15年的春节越来越近了,还有两周的工作时间貌似心已经不在异乡了,期待与家人团聚的日子,当然最后两周也得坚持站好最后一班岗,另外期待的日子往往是心里不能平静的,越是想着过年,反而日子过得越慢,于 ...

  3. java基础:简单实现线程池

    前段时间自己研究了下线程池的实现原理,通过一些源码对比,发现其实核心的东西不难,于是抽丝剥茧,决定自己实现一个简单线程池,当自已实现了出一个线程池后.发现原来那么高大上的东西也可以这么简单. 先上原理 ...

  4. Linux C 一个简单的线程池程序设计

    最近在学习linux下的编程,刚开始接触感觉有点复杂,今天把线程里比较重要的线程池程序重新理解梳理一下. 实现功能:创建一个线程池,该线程池包含若干个线程,以及一个任务队列,当有新的任务出现时,如果任 ...

  5. SpringBoot开发案例之多任务并行+线程池处理

    前言 前几篇文章着重介绍了后端服务数据库和多线程并行处理优化,并示例了改造前后的伪代码逻辑.当然了,优化是无止境的,前人栽树后人乘凉.作为我们开发者来说,既然站在了巨人的肩膀上,就要写出更加优化的程序 ...

  6. Linux C 实现一个简单的线程池

    线程池的定义 线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务.线程池线程都是后台线程.每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中.如 ...

  7. linux网络编程-一个简单的线程池(41)

    有时我们会需要大量线程来处理一些相互独立的任务,为了避免频繁的申请释放线程所带来的开销,我们可以使用线程池 1.线程池拥有若干个线程,是线程的集合,线程池中的线程数目有严格的要求,用于执行大量的相对短 ...

  8. java写的简单通用线程池demo

    首先声明,代码部分来自网络. 1.入口DabianTest: package com.lbh.myThreadPool; import java.util.ArrayList; import java ...

  9. Java一个简单的线程池实现

    线程池代码 import java.util.List; import java.util.Vector; public class ThreadPool  {     private static  ...

随机推荐

  1. NO.24两两交换链表中的节点

    NO.24两两交换链表中的节点 给定一个链表,两两交换其中相邻的节点,并返回交换后的链表. 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换. 示例:给定 1->2->3-&g ...

  2. Maven Module和Maven Project的区别

    1.maven project和module相当于父子关系.2.当新建的项目中不存在父子关系时使用project.3.当项目中存在父子关系时用project做父工程,module做子工程,module ...

  3. luogu 4587

    假设当前已经组合好了 $[1, x]$ ,设 $ans = x + 1$ :显然初始时 $x = 0, ans = 1$ 我们另 $y = \sum_{i = l} ^ {r} (w_i <= ...

  4. TCP/IP协议11种状态

    1.l  SYN_SENT :这个状态与SYN_RCVD 状态相呼应,当客户端SOCKET执行connect()进行连接时,它首先发送SYN报文,然后随即进入到SYN_SENT 状态,并等待服务端的发 ...

  5. Mac下的PHP的配置与运行

    mac 下自带apache bogon:Documents macname$ apachectl -version Server version: Apache/ (Unix) Server buil ...

  6. js 将网络图片格式转为base64 canvas 跨域

    function getBase64Image(img) { var canvas = document.createElement("canvas"); canvas.width ...

  7. BAT资深工程师由浅入深分析Tp5&Tp6底层源码☆

    第1章 课程简介 本章主要让大家知道本套课程的主线, 导学内容,如何学习源码等,看完本章要让小伙伴觉得这个是必须要掌握的,并且对加薪有很大的帮助. 第2章 [TP5灵魂]自动加载Loader 深度分析 ...

  8. class 绑定的数据对象不必内联定义在模板里

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  9. Linux fdisk命令创建逻辑分区

    [root@localhost ~]# fdisk /dev/sdb …省略部分输出… Command (m for help): n #建立新分区 Command action l logical ...

  10. yarn 单点故障 重启 ResourceManger Restart

    http://hadoop.apache.org/docs/stable/hadoop-yarn/hadoop-yarn-site/ResourceManagerRestart.html Featur ...