实现线程池
线程池的核心思路与进程池类似,主要包含一个任务队列和若干工作线程。用户将任务提交到队列中,工作线程从队列获取任务并执行。
ThreadPool 类设计
启动线程池通常分为两步:先创建线程对象,再启动线程。在构造函数中,我们需要实例化 Thread 对象。这里有一个常见的问题:成员函数作为回调时默认携带 this 指针,而线程封装的回调函数通常设计为无参无返回值的 void() 以解耦。解决此问题有两种常用方式:使用 Lambda 表达式或 std::bind 包装器。
构造函数
ThreadPool(int threadnum = default_threadnum) : _is_running(false), _threadnum(threadnum) {
for (int i = 0; i < _threadnum; i++) {
std::string name = "thread-" + std::to_string(i + 1);
// 使用 Lambda 捕获 this 指针
_threads.emplace_back([this](const std::string &name) { this->Routine(name); }, name);
}
}
Start 接口
构造函数仅创建了线程对象,并未真正启动它们。启动逻辑在 Start 方法中完成,同时需防止重复启动。
void Start() {
if (_is_running) return;
_is_running = true;
for (auto &t : _threads) {
t.Start();
}
}
Stop 和 Wait 接口的逻辑与此类似,不再赘述。
线程池接入日志
在线程创建、启动、等待及回收的关键节点输出日志,有助于排查运行时问题。ThreadPool 类同样需要遵循这一原则。
初步实现源码及效果图
以下是完整的头文件定义及主程序示例,展示了线程池的基本结构。
// ThreadPool.hpp
# once
default_threadnum = ;
< T>
{
:
{
() {
T t;
{
;
(() && _is_running) {
_wait_thread_num++;
_cond.(_lock);
_wait_thread_num--;
}
(() && !_is_running) {
;
}
t = _q.();
_q.();
}
();
}
}
:
( threadnum = default_threadnum) : _is_running(), _threadnum(threadnum), _wait_thread_num() {
( i = ; i < _threadnum; i++) {
std::string name = + std::(i + );
_threads.([]( std::string &name) { ->(name); }, name);
}
}
{
(_is_running) ;
_is_running = ;
( &t : _threads) {
t.();
}
}
{
(!_is_running) ;
_is_running = ;
(_wait_thread_num > ) {
_cond.();
}
}
{
( &t : _threads) {
t.();
}
}
{
(!_is_running) ;
{
;
_q.(t);
(_wait_thread_num > ) {
_cond.();
}
}
}
:
std::queue<T> _q;
std::vector<Thread> _threads;
_threadnum;
_wait_thread_num;
Mutex _lock;
Cond _cond;
_is_running;
};


