支持Fixed和Cached模式的线程池(C++)

支持Fixed和Cached模式的线程池

简述

任务提交和返回值获取通过std::future​和std::packaged_task​完成异步获取返回值功能。

线程执行任务的思路是:

  • 任务队列空

    • 线程池正在运行,阻塞线程

      • Cached模式视情况释放线程
    • 线程池未在运行,销毁线程

  • 任务队列不为空

    • 无论线程池是否在运行,执行任务

      • Cached模式视情况增加线程

代码

threadpool.h

#ifndef __THREADPOOL_H_
#define __THREADPOOL_H_
include<vector>
include<queue>
include<memory>
include<atomic>
include<mutex>
include<condition_variable>
include<functional>
include<iostream>
include<unordered_map>
include<future>
include<thread>
// 线程池支持的模式
enum class PoolMode
{
MODE_FIXED,
MODE_CACHED
};
// 线程类
class Thread
{
public:
using ThreadFunc = std::function<void(int)>;
Thread(ThreadFunc func);
~Thread();
void start();
int getId() const;
private:
ThreadFunc func_;
static int generateId_;
int threadId_;
};
// 线程池类
class ThreadPool
{
public:
ThreadPool();
~ThreadPool();
void setMode(PoolMode mode);
void setInitThreadSize(size_t size);
void setTaskQueMaxThreshHold(int threshhold);
void setThreadSizeThreshold(int threshold);
// 提交任务
template&lt;typename F, typename... Args&gt;
auto submitTask(F&amp;&amp; callable, Args&amp;&amp;... args)
-&gt; std::future&lt;decltype(callable(args...))&gt;
{
using Return_type = decltype(callable(args...));
auto task = std::make_shared&lt;std::packaged_task&lt;Return_type()&gt;&gt;(
std::bind(std::forward&lt;F&gt;(callable), std::forward&lt;Args&gt;(args)...)
);
std::future&lt;Return_type&gt; res = task-&gt;get_future();
{
std::unique_lock&lt;std::mutex&gt; lock(taskQueMtx_);
if (!notFull_.wait_for(lock, std::chrono::seconds(1),
[this]()-&gt;bool {return taskQue_.size() &lt; taskQueMaxThreshold_; }))
{
std::cout &lt;&lt; &quot;task submit fail!&quot; &lt;&lt; std::endl;
auto task = std::packaged_task&lt;Return_type()&gt;(
[]() {return Return_type(); }
);
auto res = task.get_future();
task();
return res;
}
taskQue_.emplace([task]() { (*task)(); });
}
return res;
}
void start(int initThreadSize = std::thread::hardware_concurrency());
ThreadPool(const ThreadPool&amp;) = delete;
ThreadPool&amp; operator=(const ThreadPool&amp;) = delete;
private:
void threadFunc(int threadid);
// 检查pool的运行状态
bool checkRunningState() const;
private:
std::unordered_map<int, std::unique_ptr<Thread>> threads_;
int initThreadSize_;
std::atomic_int curThreadSize_;
int threadSizeThreshold_;
std::atomic_int idleThreadSize_;
std::queue&lt;std::function&lt;void()&gt;&gt; taskQue_;
int taskQueMaxThreshold_;
std::mutex taskQueMtx_;
std::condition_variable notFull_;
std::condition_variable notEmpty_;
std::condition_variable exitCond_;
PoolMode poolMode_;
// 运行状态
std::atomic_bool isPoolRunning_;
};
endif
void setMode(PoolMode mode);
void setInitThreadSize(size_t size);
void setTaskQueMaxThreshHold(int threshhold);
void setThreadSizeThreshold(int threshold);
// 提交任务
template&lt;typename F, typename... Args&gt;
auto submitTask(F&amp;&amp; callable, Args&amp;&amp;... args)
-&gt; std::future&lt;decltype(callable(args...))&gt;
{
using Return_type = decltype(callable(args...));
auto task = std::make_shared&lt;std::packaged_task&lt;Return_type()&gt;&gt;(
std::bind(std::forward&lt;F&gt;(callable), std::forward&lt;Args&gt;(args)...)
);
std::future&lt;Return_type&gt; res = task-&gt;get_future();
{
std::unique_lock&lt;std::mutex&gt; lock(taskQueMtx_);
if (!notFull_.wait_for(lock, std::chrono::seconds(1),
[this]()-&gt;bool {return taskQue_.size() &lt; taskQueMaxThreshold_; }))
{
std::cout &lt;&lt; &quot;task submit fail!&quot; &lt;&lt; std::endl;
auto task = std::packaged_task&lt;Return_type()&gt;(
[]() {return Return_type(); }
);
auto res = task.get_future();
task();
return res;
}
taskQue_.emplace([task]() { (*task)(); });
}
return res;
}
void start(int initThreadSize = std::thread::hardware_concurrency());
ThreadPool(const ThreadPool&amp;) = delete;
ThreadPool&amp; operator=(const ThreadPool&amp;) = delete;
std::queue&lt;std::function&lt;void()&gt;&gt; taskQue_;
int taskQueMaxThreshold_;
std::mutex taskQueMtx_;
std::condition_variable notFull_;
std::condition_variable notEmpty_;
std::condition_variable exitCond_;
PoolMode poolMode_;
// 运行状态
std::atomic_bool isPoolRunning_;

threadpool.cpp

#include "threadpool.h"
#include<thread>
#include<memory>
const int TASK_MAX_THRESHOLD = 4;
const int THREAD_MAX_THRESHOLD = 12;
const int THREAD_MAX_IDLE_TIME = 10; // 单位:秒
ThreadPool::ThreadPool()
:initThreadSize_(0),
idleThreadSize_(0),
curThreadSize_(0),
taskQueMaxThreshold_(TASK_MAX_THRESHOLD),
threadSizeThreshold_(THREAD_MAX_THRESHOLD),
poolMode_(PoolMode::MODE_FIXED),
isPoolRunning_(false)
{}
ThreadPool::~ThreadPool()
{
isPoolRunning_ = false;
std::unique_lock<std::mutex> lock(taskQueMtx_);
notEmpty_.notify_all();
exitCond_.wait(lock, &->bool {return threads_.size() == 0; });
}
void ThreadPool::setMode(PoolMode mode)
{
if (checkRunningState())
return;
poolMode_ = mode;
}
void ThreadPool::setInitThreadSize(size_t size)
{
initThreadSize_ = size;
}
void ThreadPool::setTaskQueMaxThreshHold(int threshhold)
{
if (checkRunningState())
return;
taskQueMaxThreshold_ = threshhold;
}
void ThreadPool::setThreadSizeThreshold(int threshold)
{
if (checkRunningState())
return;
if (poolMode_ == PoolMode::MODE_CACHED)
{
threadSizeThreshold_ = threshold;
}
}
void ThreadPool::start(int initThreadSize)
{
isPoolRunning_ = true; // 设置线程池状态
initThreadSize_ = initThreadSize;
curThreadSize_ = initThreadSize;
// 创建Thread对象
for (int i = 0; i &lt; initThreadSize_; ++i)
{
auto ptr = std::make_unique&lt;Thread&gt;(std::bind(&amp;ThreadPool::threadFunc, this, std::placeholders::_1));
int threadId = ptr-&gt;getId();
threads_.emplace(threadId, std::move(ptr));
}
for (int i = 0; i &lt; initThreadSize_; ++i)
{
threads_[i]-&gt;start();
idleThreadSize_++;
}
}
void ThreadPool::threadFunc(int threadid)
{
auto lastTime = std::chrono::high_resolution_clock().now();
while (1)
{
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(taskQueMtx_);
std::cout &lt;&lt; &quot;id&quot; &lt;&lt; std::this_thread::get_id() &lt;&lt; &quot; 尝试获取任务...&quot; &lt;&lt; std::endl;
// 无任务时:线程池运行,阻塞线程,线程池终止,回收线程
while (taskQue_.size() == 0)
{
if (!isPoolRunning_) // 线程池终止,无任务,回收线程
{
// 回收线程
threads_.erase(threadid);
curThreadSize_--;
std::cout &lt;&lt; &quot;threadid: &quot; &lt;&lt; std::this_thread::get_id()
&lt;&lt; &quot; exit!&quot; &lt;&lt; std::endl;
exitCond_.notify_all();
return;
}
// cached模式,任务队列为空:超时回收线程,未超时阻塞线程
if (poolMode_ == PoolMode::MODE_CACHED)
{
// 判断是否是超时唤醒
if (std::cv_status::timeout ==
notEmpty_.wait_for(lock, std::chrono::seconds(1)))
{
auto now = std::chrono::high_resolution_clock().now();
auto dur = std::chrono::duration_cast&lt;std::chrono::seconds&gt;(now - lastTime);
if (dur.count() &gt;= THREAD_MAX_IDLE_TIME
&amp;&amp; curThreadSize_ &gt; initThreadSize_)
{
// 回收线程:
threads_.erase(threadid);
curThreadSize_--;
idleThreadSize_--;
std::cout &lt;&lt; &quot;threadid: &quot; &lt;&lt; std::this_thread::get_id()
&lt;&lt; &quot; exit!&quot; &lt;&lt; std::endl;
return;
}
}
}
else // fixed模式,任务队列为空:阻塞线程
{
notEmpty_.wait(lock);
}
}
// 从任务队列中取任务
task = taskQue_.front();
taskQue_.pop();
idleThreadSize_--;
std::cout &lt;&lt; &quot;id&quot; &lt;&lt; std::this_thread::get_id() &lt;&lt; &quot; 获取任务成功!&quot; &lt;&lt; std::endl;
if (taskQue_.size() &gt; 0)
{
notEmpty_.notify_all();
}
// 增加线程
if (poolMode_ == PoolMode::MODE_CACHED
&amp;&amp; taskQue_.size() &gt; idleThreadSize_
&amp;&amp; curThreadSize_ &lt; threadSizeThreshold_)
{
std::cout &lt;&lt; &quot;add new thread!&quot; &lt;&lt; std::endl;
auto ptr = std::make_unique&lt;Thread&gt;(std::bind(&amp;ThreadPool::threadFunc, this, std::placeholders::_1));
int threadId = ptr-&gt;getId();
threads_.emplace(threadId, std::move(ptr));
threads_[threadId]-&gt;start();
curThreadSize_++;
idleThreadSize_++;
}
notFull_.notify_all();
}
if (task != nullptr)
{
task();
}
idleThreadSize_++;
lastTime = std::chrono::high_resolution_clock().now(); // 更新lastTime
}
}
bool ThreadPool::checkRunningState() const
{
return isPoolRunning_;
}
// Thread
int Thread::generateId_ = 0; // 通过generateId_++ 来生成threadId_
Thread::Thread(ThreadFunc func)
:func_(func)
,threadId_(generateId_++)
{}
Thread::~Thread()
{
}
void Thread::start()
{
std::thread t(func_, threadId_);
t.detach(); // 设置分离线程
}
int Thread::getId() const
{
return threadId_;
}
isPoolRunning_ = true; // 设置线程池状态
initThreadSize_ = initThreadSize;
curThreadSize_ = initThreadSize;
// 创建Thread对象
for (int i = 0; i &lt; initThreadSize_; ++i)
{
auto ptr = std::make_unique&lt;Thread&gt;(std::bind(&amp;ThreadPool::threadFunc, this, std::placeholders::_1));
int threadId = ptr-&gt;getId();
threads_.emplace(threadId, std::move(ptr));
}
for (int i = 0; i &lt; initThreadSize_; ++i)
{
threads_[i]-&gt;start();
idleThreadSize_++;
}
std::cout &lt;&lt; &quot;id&quot; &lt;&lt; std::this_thread::get_id() &lt;&lt; &quot; 尝试获取任务...&quot; &lt;&lt; std::endl;
// 无任务时:线程池运行,阻塞线程,线程池终止,回收线程
while (taskQue_.size() == 0)
{
if (!isPoolRunning_) // 线程池终止,无任务,回收线程
{
// 回收线程
threads_.erase(threadid);
curThreadSize_--;
std::cout &lt;&lt; &quot;threadid: &quot; &lt;&lt; std::this_thread::get_id()
&lt;&lt; &quot; exit!&quot; &lt;&lt; std::endl;
exitCond_.notify_all();
return;
}
// cached模式,任务队列为空:超时回收线程,未超时阻塞线程
if (poolMode_ == PoolMode::MODE_CACHED)
{
// 判断是否是超时唤醒
if (std::cv_status::timeout ==
notEmpty_.wait_for(lock, std::chrono::seconds(1)))
{
auto now = std::chrono::high_resolution_clock().now();
auto dur = std::chrono::duration_cast&lt;std::chrono::seconds&gt;(now - lastTime);
if (dur.count() &gt;= THREAD_MAX_IDLE_TIME
&amp;&amp; curThreadSize_ &gt; initThreadSize_)
{
// 回收线程:
threads_.erase(threadid);
curThreadSize_--;
idleThreadSize_--;
std::cout &lt;&lt; &quot;threadid: &quot; &lt;&lt; std::this_thread::get_id()
&lt;&lt; &quot; exit!&quot; &lt;&lt; std::endl;
return;
}
}
}
else // fixed模式,任务队列为空:阻塞线程
{
notEmpty_.wait(lock);
}
}
// 从任务队列中取任务
task = taskQue_.front();
taskQue_.pop();
idleThreadSize_--;
std::cout &lt;&lt; &quot;id&quot; &lt;&lt; std::this_thread::get_id() &lt;&lt; &quot; 获取任务成功!&quot; &lt;&lt; std::endl;
if (taskQue_.size() &gt; 0)
{
notEmpty_.notify_all();
}
// 增加线程
if (poolMode_ == PoolMode::MODE_CACHED
&amp;&amp; taskQue_.size() &gt; idleThreadSize_
&amp;&amp; curThreadSize_ &lt; threadSizeThreshold_)
{
std::cout &lt;&lt; &quot;add new thread!&quot; &lt;&lt; std::endl;
auto ptr = std::make_unique&lt;Thread&gt;(std::bind(&amp;ThreadPool::threadFunc, this, std::placeholders::_1));
int threadId = ptr-&gt;getId();
threads_.emplace(threadId, std::move(ptr));
threads_[threadId]-&gt;start();
curThreadSize_++;
idleThreadSize_++;
}
notFull_.notify_all();
}
if (task != nullptr)
{
task();
}
idleThreadSize_++;
lastTime = std::chrono::high_resolution_clock().now(); // 更新lastTime
}

test.cpp

#include"threadpool.h"
#include<chrono>
int main()
{
{
ThreadPool pool;
pool.setMode(PoolMode::MODE_CACHED);
pool.setTaskQueMaxThreshHold(20);
pool.start(2);
std::future<int> res1 = pool.submitTask([](int a, int b)->int {std::this_thread::sleep_for(std::chrono::seconds(2)); return a + b; }, 1, 2);
auto res2 = pool.submitTask([](int a, int b, int c)->int {std::this_thread::sleep_for(std::chrono::seconds(3)) ; return a + b + c; }, 1, 2, 3);
auto res3 = pool.submitTask([](int a, int b, int c)->int {std::this_thread::sleep_for(std::chrono::seconds(3)); return a * b * c; }, 1, 2, 3);
auto res4 = pool.submitTask([](int a, int b, int c)->int {std::this_thread::sleep_for(std::chrono::seconds(3)); return a * b * c; }, 1, 2, 3);
auto res5 = pool.submitTask([](int a, int b, int c)->int {std::this_thread::sleep_for(std::chrono::seconds(3)); return a * b * c; }, 1, 2, 3);
auto res6 = pool.submitTask([](int a, int b, int c)->int {std::this_thread::sleep_for(std::chrono::seconds(3)); return a * b * c; }, 1, 2, 3);
auto res7 = pool.submitTask([](int a, int b, int c)->int {std::this_thread::sleep_for(std::chrono::seconds(3)); return a * b * c; }, 1, 2, 3);
auto res8 = pool.submitTask([](int a, int b, int c)->int {std::this_thread::sleep_for(std::chrono::seconds(3)); return a * b * c; }, 1, 2, 3);
std::cout &lt;&lt; res1.get() &lt;&lt; std::endl;
std::cout &lt;&lt; res2.get() &lt;&lt; std::endl;
std::cout &lt;&lt; res3.get() &lt;&lt; std::endl;
std::cout &lt;&lt; res4.get() &lt;&lt; std::endl;
std::cout &lt;&lt; res5.get() &lt;&lt; std::endl;
std::cout &lt;&lt; res6.get() &lt;&lt; std::endl;
std::cout &lt;&lt; res7.get() &lt;&lt; std::endl;
std::cout &lt;&lt; res8.get() &lt;&lt; std::endl;
}
std::cout &lt;&lt; &quot;over!&quot; &lt;&lt; std::endl;
getchar();
return 0;
std::cout &lt;&lt; res1.get() &lt;&lt; std::endl;
std::cout &lt;&lt; res2.get() &lt;&lt; std::endl;
std::cout &lt;&lt; res3.get() &lt;&lt; std::endl;
std::cout &lt;&lt; res4.get() &lt;&lt; std::endl;
std::cout &lt;&lt; res5.get() &lt;&lt; std::endl;
std::cout &lt;&lt; res6.get() &lt;&lt; std::endl;
std::cout &lt;&lt; res7.get() &lt;&lt; std::endl;
std::cout &lt;&lt; res8.get() &lt;&lt; std::endl;
}
std::cout &lt;&lt; &quot;over!&quot; &lt;&lt; std::endl;
getchar();
return 0;
}

posted @   贰拾散人  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示