前面我们实现了CallBack类,实现了对任意可调用对象的封装,且统一了调用接口。

现在利用CallBack类,我们来实现一个线程池,我们的线程池包含:

1. 状态机, 用于控制和管理线程池的运行、停止

2. 任务队列, std::queue< std::unique_ptr< Base::Closure > > tasks;   维护了一个queue来保存、添加、删除需要执行的任务(用户定义回调函数,封装成CallBack统一接口)

3. 存放运行线程的容器, std::vector< std::thread > workers; 每次新建一个线程执行一个task,并将该线程放入vector里,便于对线程进行统一的管理

4. 同步锁和条件变量:

std::mutex worker_mutex; // 用于多个线程访问workers时的同步
       std::mutex queue_mutex; // 用于多个线程访问tasks时的同步
       std::condition_variable condition; // 用于添加任务线程与执行线程之间的协作,以及用于状态机改变时的协作等

#include <functional>
#include<mutex>
#include<vector>
#include<queue>
#include<condition_variable>
#include<thread>
#include<memory>
#include<future>
#include<iostream> namespace Base { class CallBack; typedef CallBack Closure; class CallBack {
public:
template<class F, class... Args>
CallBack(F&& f, Args&&... args) {
task = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
} void Run() {
task();
} virtual ~CallBack() {} private:
std::function<void()> task;
}; template<class F, class... Args>
CallBack* NewCallBack(F&& f, Args&&... args) {
return new CallBack(f, args...);
} } // namespace Base class FixedThreadPool{
public:
enum State{
IDLE,
RUNNING,
STOP,
}; FixedThreadPool();
FixedThreadPool( size_t );
FixedThreadPool ( const FixedThreadPool& ) = delete ;
FixedThreadPool& operator=( const FixedThreadPool& ) = delete ; void SetPoolSize( size_t );
size_t Size() const; void AddTask( Base::Closure* task );
~FixedThreadPool(); void AwaitTermination();
void Start();
void Stop(); private:
void ThreadWorker(); std::vector< std::thread > workers; // 用于放置运行线程 std::queue< std::unique_ptr< Base::Closure > > tasks; // 任务队列 std::mutex worker_mutex;
std::mutex queue_mutex;
std::condition_variable condition; State state_;
unsigned int thread_size_;
}; FixedThreadPool::FixedThreadPool():
state_(IDLE),
thread_size_(4)
{
} FixedThreadPool::FixedThreadPool(size_t num_threads):
state_(IDLE),
thread_size_(num_threads)
{
} void FixedThreadPool::SetPoolSize(size_t size) {
thread_size_ = size;
} size_t FixedThreadPool::Size() const {
return thread_size_;
} // Destructor joins all threads
FixedThreadPool::~FixedThreadPool() {
for(std::thread &worker: workers) {
if (worker.joinable()) {
worker.join();
}
}
} // 根据线程池的size(也就是线程数),与任务数量,创建恰当数量的线程,分别从tasks队列里取出任务执行.
// 并把这些线程都放进workers容器里,方便统一管理
void FixedThreadPool::Start() {
{
std::unique_lock<std::mutex> lock(queue_mutex);
state_ = RUNNING;
unsigned int num_working_threads_ =
thread_size_ < tasks.size()? thread_size_ : tasks.size();
for (unsigned int i = workers.size(); i < num_working_threads_; i++) {
workers.emplace_back(std::thread(&FixedThreadPool::ThreadWorker, this));
}
}
condition.notify_all();
} // Thread worker 从tasks队列里取一个任务(回调函数),执行
void FixedThreadPool::ThreadWorker() {
Base::Closure* task;
while (1) {
{
std::unique_lock<std::mutex> lock(queue_mutex);
condition.wait(lock,
[this] { return state_ == STOP || !tasks.empty(); }); // state_==STOP && tasks==empty , state_!=STOP && tasks!=empty, state_==STOP && tasks != empty 继续往下执>行; state_ != STOP && tasks==empty 阻塞等待
if (state_ == STOP && tasks.empty()) {
return;
}
task = (tasks.front()).release();
tasks.pop();
}
task->Run();
}
} // Add new work item to the pool
// 如果当前工作线程数小于线程池的size, 就新建一个线程,从tasks队列取出一个任务并执行,并将该线程放入workers容器
// 然后将该任务task 放进tasks系列, 最后notify_one, 唤醒一个执行线程去执行任务
void FixedThreadPool::AddTask(Base::Closure* task) {
{
std::unique_lock<std::mutex> lock(worker_mutex);
if (state_ == RUNNING && workers.size() < thread_size_) {
workers.emplace_back(std::thread(&FixedThreadPool::ThreadWorker, this));
}
} {
std::unique_lock<std::mutex> lock(queue_mutex);
if (state_ == STOP) {
throw std::runtime_error("enqueue on stopped ThreadPool");
}
tasks.emplace(std::unique_ptr<Base::Closure>(task));
}
condition.notify_one();
} // Blocks and wait for all previously submitted tasks to be completed.
void FixedThreadPool::AwaitTermination() {
condition.notify_all();
for(std::thread &worker: workers) {
if (worker.joinable()) {
worker.join();
}
}
} // Shut down the threadpool. This method does not wait for previously submitted
// tasks to be completed.
void FixedThreadPool::Stop() {
{
std::unique_lock<std::mutex> lock(queue_mutex);
state_ = STOP;
}
condition.notify_all();
} } int func( int a ){
std::this_thread::sleep_for( std::chrono::seconds(100) );
printf(" a = %d\n", a);
return 0;
} void testThreadpool(){
FixedThreadPool pool(4);
pool.Start();
pool.AddTask(Base::NewCallBack(func, 3));
/*for(int i = 0; i < 10; ++i) {
pool.AddTask(new Base::CallBack([i] {
std::cout << "hello " << i << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "world " << i << std::endl;
return i*i;
}));
}*/ pool.Stop();
pool.AwaitTermination();
std::cout << "All tasks complted." << std::endl; } int main(){
testThreadpool(); // 该测试用例先新建了一个能包含4个线程的线程池,首先往里面添加一个任务,该任务是由CallBack封装的普通函数,
return 0; // 先睡1秒,再打印参数3, 线程池分配一个线程来执行这个任务。然后用10次循环来添加任务,该任务是由CallBack封装的
// lamda表达式,依次打印hello 循环下标, world 循环下标。由于线程池最多只能开4个线程,再依次继续新建3个线程执行
} // 执行任务以后,剩下的任务只能先入任务队列并等待之前已经新建的线程来取任务执行。

运行结果:

[daq@centos build]$ ./hello-exe/cmake-good
hello hello 10
world 1
hello 3
world 3
hello 4
world 4
hello 5
world 5
hello 6
world 6
hello 7
world 7
hello 8
world 8
hello hello 9
world 9 world 0
2
world 2
a = 3
All tasks complted.

  

一个线程池的c++实现的更多相关文章

  1. 二 Java利用等待/通知机制实现一个线程池

    接着上一篇博客的 一Java线程的等待/通知模型 ,没有看过的建议先看一下.下面我们用等待通知机制来实现一个线程池 线程的任务就以打印一行文本来模拟耗时的任务.主要代码如下: 1  定义一个任务的接口 ...

  2. ExecutorService实际上是一个线程池的管理工具

    在Java5之后,并发线程这块发生了根本的变化,最重要的莫过于新的启动.调度.管理线程的一大堆API了.在Java5以后,通过Executor来启动线程比用 Thread的start()更好.在新特征 ...

  3. python创建一个线程和一个线程池

    创建一个线程 1.示例代码 import time import threading def task(arg): time.sleep(2) while True: num = input('> ...

  4. Android 性能优化(16)线程优化:Creating a Manager for Multiple Threads 如何创建一个线程池管理类

    Creating a Manager for Multiple Threads 1.You should also read Processes and Threads The previous le ...

  5. 死磕 java线程系列之自己动手写一个线程池

    欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. (手机横屏看源码更方便) 问题 (1)自己动手写一个线程池需要考虑哪些因素? (2)自己动手写 ...

  6. 死磕 java线程系列之自己动手写一个线程池(续)

    (手机横屏看源码更方便) 问题 (1)自己动手写的线程池如何支持带返回值的任务呢? (2)如果任务执行的过程中抛出异常了该怎么处理呢? 简介 上一章我们自己动手写了一个线程池,但是它是不支持带返回值的 ...

  7. 手写一个线程池,带你学习ThreadPoolExecutor线程池实现原理

    摘要:从手写线程池开始,逐步的分析这些代码在Java的线程池中是如何实现的. 本文分享自华为云社区<手写线程池,对照学习ThreadPoolExecutor线程池实现原理!>,作者:小傅哥 ...

  8. [转]使用VC/MFC创建一个线程池

    许多应用程序创建的线程花费了大量时间在睡眠状态来等待事件的发生.还有一些线程进入睡眠状态后定期被唤醒以轮询工作方式来改变或者更新状态信息.线程池可以让你更有效地使用线程,它为你的应用程序提供一个由系统 ...

  9. 在Linux下写一个线程池以及线程池的一些用法和注意点

    -->线程池介绍(大部分来自网络)  在这个部分,详细的介绍一下线程池的作用以及它的技术背景以及他提供的一些服务等.大部分内容来自我日常生活中在网络中学习到的一些概念性的东西. -->代码 ...

  10. java如何自定义一个线程池

    java线程池的一些简单功能,后续会更新,代码不多,很好理解 package com.rbac.thread; import java.util.ArrayList; import java.util ...

随机推荐

  1. Docker CLI docker buildx bake 常用命令

    Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux或Windows操作系统的机器上,也可以实现虚拟化.Docker是内核 ...

  2. python_test_5001_Moudle_pandas

    import pandas as pd import numpy as np from lib_001_decorator_log_funcname import decorator_log_func ...

  3. windows下MinGW64编译环境设置

    windows下MinGW64编译环境设置 1. MinGW 介绍 MinGW 的全称是:Minimalist GNU on Windows .是将经典的开源 C语言 编译器 GCC 移植到了 Win ...

  4. 并发多线程学习(六)Java线程间的通信

    合理的使用Java多线程可以更好地利用服务器资源.一般来讲,线程内部有自己私有的线程上下文,互不干扰.但是当我们需要多个线程之间相互协作的时候,就需要我们掌握Java线程的通信方式.本文将介绍Java ...

  5. CMMI-QA工作流程(角色区分)

    qa 是如何工作的,如何保证产品质量的? 首先制定质量保证计划->根据过程清单和产品清单对组织级和项目级内容进行检查->不符合项记录在不符合项问题记录表中.反馈项目精力,跟踪问题知道问题解 ...

  6. easyui combobox两种不同的数据加载方式

    1.通过http访问加载数据. $('#XXXId').combobox({ url: httpUrl, valueField: 'code', textField: 'name', });2.通过j ...

  7. 《Vue.js 3.x高效前端开发(视频教学版)》源码课件同步教学视频免费下载

    <Vue.js 3.x高效前端开发(视频教学版)>源码课件同步教学视频免费下载.获得出版社和作者授权,可用于个人学习使用,禁止任何形式的商用.

  8. Kubernetes学习笔记(一)

    参考: kubectl Cheat Sheet | Kubernetes Kubernetes kubectl 命令表 _ Kubernetes(K8S)中文文档_Kubernetes中文社区 Pla ...

  9. vi 快捷键/ctags

    vi 配置 syntax enableset nu set relativenumberset hlsearch set autoindentset shiftwidth=4set tabstop=4 ...

  10. 清空kafka全部数据

    1.停止机器上的kafka,停止业务系统 docker容器执行命令: docker stop 容器名称 2.删除kafka存储目录(server.properties文件log.dirs配置,默认为& ...