前面我们实现了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. springboot gradle 加速问题

    初始化项目使用阿里云 seriver url : https://start.spring.io 直接修改为: https://start.aliyun.com 关键的gradle 修改安装包地址 g ...

  2. 记录一次antd升级到最新版本,与现有代码冲突导致的问题

    背景:发版的前一夜,测试突然发现项目某个功能点击弹框会导致整个页面直接空白,立即提了个单要我赶紧修复.(内心真是一万个卧槽)本来准备不加班的.没办法,那只能解决.第一步就怀疑是不是谁动了代码,毕竟一两 ...

  3. [Nginx]status:203 Failed to start The NGINX HTTP and reverse proxy server

    怎么感觉Linux的nGinx比Win的事一个一个一个的多啊(半恼) 运行systemctl status nginx时提示: ① Process: 123456 ExecStartPre=/usr/ ...

  4. 「部署日记」Android Studio乱码解决方案

    弄了一台新电脑,第一件事肯定是弄好打造台啦 于是VS.AS.CRD.NSIS.Adobe全家桶全安装完毕, 问题来了,在打开Android Studio时,出现乱码,比如 这样的: 这样的: 这样的: ...

  5. 如何为 Debian 11 安装图形用户界面 (GUI)

    如何为 Debian 11 安装图形用户界面 (GUI) allway2 于 2021-12-26 17:30:14 发布 11767 收藏 23文章标签: debian 服务器 linux版权 华为 ...

  6. 搭建Kubord管理k8s/EKS以及Harbor私有仓库教程

    eks首先要去aws后台进行创建,这里不再讲解详细的过程,下面讲解如果通过命令行以及kuboard调度esk服务. 安装docker以及docker-compose yum install docke ...

  7. Jenkins拉取GitHub上代码

    1.github 生成 Personal Access Token 2.github 设置 GitHub webhooks (具体需要持续集成的项目),新建或者设置现有项目的 webhooks 选项, ...

  8. qt creator 在ubuntu22.04下显示不正常处理

    打开:sudo vim /etc/gdm3/custom.conf 找到:#WaylandEnable=false 去掉注释,问题解决

  9. saml login的流程

    用户会访问首页/, 然后进入到指定的一个URL, 就是admin在site-settings里面的设置的那个地址, 发现权限不够,进入到403accesslogin, 然后调用user4032logi ...

  10. Ubuntu16.04设置root以及root用户自动登录

    自己之前做的笔记确实不是很详细.因为像是一些环境的配置,即使是这样做,但是自己大概率下是不知道这样做的原因,所以很多东西能不能弄好是有很大的运气成分在里面.所以就很需要记录下自己到底干了些什么. 所以 ...