前面我们实现了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. 用“餐厅打包”的故事说明白Python里面的自定义函数

    注:博主并非Python专业程序员,年龄12岁,Python龄不到1岁,才疏学浅,如有错误还请大佬指教! 希望能通过本专栏帮助到一些Python小白! 嗨~大家好!上篇博文咱们说了,万一有一些上万行才 ...

  2. 注释:MARK与TODO、FIXME

    MARK: 在OC中的用法: #pragma mark -说明文字(可以不加-) 在swift中的用法:// MARK: - 说明文字(可以不加-) TODO.FIXME(不区分OC.swift) / ...

  3. 【uboot 】uboot通过tftp下载内核

    1.开发板uboot,虚拟机能相互ping通 2.ubuntu搭建好tftp服务器,设置好文件夹,放置好文件 sudo apt install tftpd-hpa  //安装服务程序 sudo sys ...

  4. Self-Attention学习

    2个连接+1个视频推荐 Self-Attention 原理与代码实现_DonngZH的博客-CSDN博客_selfattention代码 Transformer模型详解(图解最完整版) - 知乎 (z ...

  5. reduce()

    from functools import reducea=[10,11,22,33]b=reduce(lambda x,y:x+y,a)<===>sum(a)print(b)

  6. const char* str和const char str[]的区别

    首先,字符串常量是存储在flash中的.假设字符串常量在flash中的地址是0x8003fb8. 第一种方式,str等价于str的内存单元的地址,str的内存单元存储着字符串常量的地址 第二种方式,s ...

  7. oracle排查慢sql

    查询最慢的SQL select * from (select sa.SQL_TEXT, sa.SQL_FULLTEXT, sa.EXECUTIONS "执行次数", round(s ...

  8. qt 单元测试遇到的问题

    升级了qt creator到新版本.使用 google 单元测试,发现一个单元测试显示不对了. 测试结果输出:FATAL, 项目"t_ps"的测试未产生任何预期输出 比较了几个测试 ...

  9. c#笔记(四)——switch

    ---恢复内容开始--- using UnityEngine; using System.Collections;   public class Script1 : MonoBehaviour {   ...

  10. 《Python 3网络爬虫实战》示例源码免费下载

    #好书推荐##好书奇遇季#<Python 3网络爬虫实战>京东当当天猫都有发售. https://item.jd.com/12936936.html 本书配套示例源码,文后提供了下载二维码 ...