本文使用C++11的线程、互斥量和条件变量,实现了一个轻巧的线程池,可用于大量并发任务的场景,以避免频繁的线程创建和销毁,节约系统资源。
本文对处理任务的接口,也进行了包装,可接受:函数对象、成员函数、普通函数、fucntion和lamda表达式,便于完美的应用到实际工作中。
本文源码见【完整代码】章节,或GitHub:https://github.com/deargo/cpphelper。
线程池介绍
在处理大量并发任务的时候,如果按照传统的方式,一个请求一个线程来处理请求任务,大量的线程创建和销毁,将消耗过多的系统资源,还增加了线程上下文切换的开销,而通过线程池技术,就可以很好的解决这些问题。
线程池技术通过在系统中预先创建一定数量的线程,当任务请求到来时从线程池中,分配一个预先创建的线程去处理任务,线程在完成任务之后,还可以重用,不会销毁,而是等待下次任务的到来。
线程池分为半同步半异步线程池和领导者追随者线程池,本文将介绍的是前者。半同步半异步线程池分为三层:
- 同步服务层:它处理来自上层的任务请求,上层的请求可能是并发的,这些请求不是马上就会被处理的,而是将这些任务放到一个同步排队层中,等待处理。
- 同步排队层:来自上层的任务请求,都会加到排队层中,等待处理。
- 异步服务层:这一层中会有多个线程,同时处理排队层中的任务,异步服务层从同步排队层中取出任务,并行的处理。
关键技术分析
通过上一节我们知道,排队层居于核心地位,因为上层会将任务添加到排队层中,下层同时也会取出任务,这里有一个同步的过程。
在实现时,排队层就是一个同步队列,允许多个线程同时去添加,或取出任务,并且要保障操作过程是安全的。
线程池有两个活动过程,一个是往同步队列中添加任务的过程,另一个是从同步队列中取出任务的过程。
线程池需启动一定数量的异步线程,来并行处理排队层中的任务,如果排队层中的任务为空,则需等待任务的到来,如果发现有新任务,则会唤醒一个线程来处理。
同步服务层会不断的将新的任务,添加到同步排队层中。当任务非常多又耗时时,异步线程处理不过来,则会导致排队层任务过多,内存暴涨,因此需要添加上限,避免此问题。
另外,对于任务类型,与线程池,本是不相关的,但考虑到实际运用,任务类型应支持:函数对象、成员函数、普通函数、fucntion和lamda表达式等,于是结合我的另外一篇博客《C++Helper--用C++11改进命令模式》,可对添加任务的接口,进行优化。
完整代码
#pragma once
#if _MSC_VER >= 1600
#pragma execution_character_set("utf-8")
#endif
#include <list>
#include <mutex>
#include <thread>
#include <condition_variable>
#include <iostream>
#include <memory>
#include <atomic>
#include <functional>
using namespace std;
namespace CppHelper
{
class CThreadPool
{
private:
using Locker = std::unique_lock<std::mutex>;
using Guard = std::lock_guard<std::mutex>;
//同步队列
template <typename Task>
class SyncQueue
{
public:
using Tasks = std::list<Task>;
SyncQueue(int maxSize): m_maxSize(maxSize), m_needStop(false) { }
~SyncQueue(){ resize(0);}
//添加事件
bool put(const Task& task)
{
return add(task);
}
//添加事件
bool put(Task && task)
{
//调用内部接口,进行完美转发
return add(std::forward<Task>(task));
}
//从队列中取事件,取所有事件
bool take(Tasks &tasks)
{
Locker locker(m_mutex);
//当不满足任何一个则等待,但是若m_needStop为true是因为任务要终止了所以不阻塞
m_notEmpty.wait(locker, [this]{return (m_needStop || notEmpty()); });
if (m_needStop)
{
return false;
}
tasks = std::move(m_tasks);
m_notFull.notify_one();
return true;
}
//取一个事件
bool take(Task &task)
{
Locker locker(m_mutex);
m_notEmpty.wait(locker, [this]{return m_needStop || notEmpty(); });
if (m_needStop)
{
return false;
}
task = m_tasks.front();
m_tasks.pop_front();
m_notFull.notify_one();
return true;
}
//终止同步队列
void stop()
{
{
//锁作用域就在这对大括号内
Guard guard(m_mutex);
//将终止标志设为true
m_needStop = true;
}
//唤醒所有进程一一终止
m_notFull.notify_all();
m_notEmpty.notify_all();
}
//队列为空
bool empty()
{
Guard guard(m_mutex);
return m_tasks.empty();
}
//队列为满
bool full()
{
Guard guard(m_mutex);
return m_tasks.size() == m_maxSize;
}
//队列容量
typename Tasks::size_type capacity()
{
Guard guard(m_mutex);
return m_maxSize;
}
//队列大小
typename Tasks::size_type size()
{
Guard guard(m_mutex);
return m_tasks.size();
}
//重置队列大小
void resize(int maxSize)
{
stop();
Locker locker(m_mutex);
m_tasks.clear();
m_maxSize = maxSize;
}
//清空队列
void clear()
{
stop();
Locker locker(m_mutex);
m_tasks.clear();
}
private:
//队列不为满
bool notFull() const
{
bool full = (m_tasks.size() >= m_maxSize);
if (full)
{
cout << "the queue is full, need wait..." << endl;
}
return !full;
}
//队列不为空
bool notEmpty() const
{
bool empty = m_tasks.empty();
if (empty)
{
cout << "asynchronous thread["<<this_thread::get_id() << "]: the queue is empty, need wait." << endl;
}
return !empty;
}
//向队列中添加任务,若不为满且终止标志为false则添加事件
bool add(Task && task)
{
Locker locker(m_mutex);
//当不满足任何一个则等待,但是若m_needStop为true是因为任务要终止了所以不阻塞
m_notFull.wait(locker, [this]{return m_needStop || notFull(); });
if (m_needStop)
{
return false;
}
m_tasks.emplace_back(std::forward<Task>(task));
m_notEmpty.notify_one();
return true;
}
private:
//缓冲区
Tasks m_tasks;
//互斥量和条件变量结合起来使用
std::mutex m_mutex;
//队列不为空的条件变量
std::condition_variable m_notEmpty;
//队列不为满的条件变量
std::condition_variable m_notFull;
//同步队列最大长度
size_t m_maxSize;
//终止的标识,当为true时代表同步队列要终止
bool m_needStop;
};
private:
using Ret = void;
using Task = std::function<Ret()>;
using Tasks = std::list<Task>;
public:
//传递给同步队列的最大个数
enum { DEFAULT_MAX_TASK_COUNT = 1000 };
//构造函数,默认处理线程数为CPU核心数量
CThreadPool(int handleTheadCount = std::thread::hardware_concurrency()):
m_handle_count(handleTheadCount),m_queue(CThreadPool::DEFAULT_MAX_TASK_COUNT)
{
start();
}
~CThreadPool()
{
//如果没有停止,则主动停止线程池
stop();
}
//启动线程池(默认已启动)
void start()
{
Locker locker(m_mutex);
if(m_running)
{
return;
}
run();
}
//终止所有任务的执行
void stop()
{
Locker locker(m_mutex);
if(false == m_running)
{
return;
}
//终止同步队列
m_queue.stop();
m_running = false;
for (auto thread : m_threadgroup)
{
if (thread)
{
thread->join();
}
}
m_threadgroup.clear();
}
//添加任务:接受返回类型为void类型的function、函数对象、lamda和普通函数
template< class Func, class... Args, class = typename std::enable_if<!std::is_member_function_pointer<Func>::value>::type>
bool addTask(Func && func, Args && ... args)
{
return m_queue.put([&func, args...]{return func(args...); });
}
//添加任务:接受返回类型为void类型的const成员函数
template<class CObj, class... DArgs, class PObj, class... Args>
bool addTask(Ret(CObj::*func)(DArgs...) const, PObj && pObj, Args && ... args)
{
return m_queue.put([&, func]{return (*pObj.*func)( args...); });
}
//添加任务:接受返回类型为void类型的non-const类型成员函数
template<class CObj, class... DArgs, class PObj, class... Args>
bool addTask(Ret(CObj::*func)(DArgs...), PObj && pObj, Args && ... args)
{
return m_queue.put([&, func]{return (*pObj.*func)( args...); });
}
//队列接口
bool queueEmpty() { return m_queue.empty();}
bool queueFull() { return m_queue.full();}
typename Tasks::size_type queueCapacity() { return m_queue.capacity();}
typename Tasks::size_type queueSize() { return m_queue.size();}
void queueResize(int maxSize) { return m_queue.resize(maxSize);}
void queueClear() { return m_queue.clear();}
//处理接口
bool isRunning() { Guard guard(m_mutex); return m_running;}
int handleCount() {Guard guard(m_mutex); return m_handle_count;}
void handleCountResize(int handleTheadCount)
{
stop();
Locker locker(m_mutex);
m_handle_count = handleTheadCount;
run();
}
private:
void run()
{
m_running = true;
//建立numThreads个数的线程组
for (int i = 0; i < m_handle_count; i++)
{
//多个线程依次的处理
m_threadgroup.emplace_back(std::make_shared<std::thread>(&CThreadPool::runInThread, this));
}
}
void runInThread()
{
while (m_running)
{
Tasks list;
m_queue.take(list);
//取出任务队列中的全部,依次执行
for (auto & task : list)
{
if (!m_running)
{
return ;
}
//执行任务
task();
}
}
return;
}
private:
std::mutex m_mutex;
int m_handle_count;
//处理任务的线程组
std::list<std::shared_ptr<std::thread>> m_threadgroup;
//同步队列
SyncQueue<Task> m_queue;
//运行的标志,flase代表终止
atomic_bool m_running;
//保证在函数在多线程环境中只被调用一次
std::once_flag m_flag;
};
}
测试示例
在本示例中,线程池初始将创建两个线程,然后外部线程将不停的向线程池中,添加新任务,线程池中的异步线程,将会并行的处理同步队列中的任务。
测试代码
CppHelper::CThreadPool pool(2);
std::thread thd1([&pool]
{
auto thdid = this_thread::get_id();
for (int i = 0; i < 10; i++)
{
pool.addTask([](std::thread::id thdid, int value)
{
cout << "synchronous thread1[" << thdid << "] value[" << value << "], handle thread: " << this_thread::get_id() << endl;
},thdid,i+100);
}
});
std::thread thd2([&pool]
{
auto thdid = this_thread::get_id();
for (int i = 0; i < 10; i++)
{
pool.addTask([&](std::thread::id thdid, int value)
{
cout << "synchronous thread2[" << thdid << "] value[" << value << "], handle thread: " << this_thread::get_id() << endl;
},thdid,i+200);
}
});
cout << "sleep seconds(2) ..." << endl << endl;
this_thread::sleep_for(std::chrono::seconds(2));
cout << endl << "input any words to stop: ";
getchar();
pool.stop();
thd1.join();
thd2.join();
测试结果
参考资料及源码:
参考书籍:《深入应用C++11:代码优化与工程级应用》。