ThreadPool - 高并发任务调度线程池
HSLL::ThreadPool 是基于HSLL::BlockQueue实现的弹性任务调度系统,采用批量处理优化和智能任务分发机制。提供多种任务提交模式,适用于高吞吐量、低延迟的并行计算场景。
依赖于HSLL::BlockQueue :BlockQueue - 高效线程安全阻塞队列
您的star
是对该项目的肯定
核心特性
1. 智能任务调度
- 双模式工作线程:
- 单任务模式:逐条处理保证最低延迟
- 批量模式:单次处理最多batchSize个任务,提升吞吐量
- 自适应负载均衡:基于BlockQueue实现自动背压控制
2. 多样化提交接口
提交方式 | 非阻塞 | 无限等待 | 超时等待 |
---|---|---|---|
单任务提交 | append() | wait_append() | wait_append(timeout) |
单任务原位构造 | emplace() | wait_emplace() | wait_emplace(timeout) |
批量提交 | appendBulk() | wait_appendBulk() | wait_appendBulk(timeout) |
批量原位构造 | emplaceBulk() | wait_emplaceBulk() | wait_emplaceBulk(timeout) |
3. 高效资源管理
- 预分配任务队列:初始化时固定队列容量,消除运行时内存分配
- 零拷贝任务传递:支持原位构造和完美转发
- 优雅关闭机制:立即停止信号+队列耗尽处理
使用示例
// 定义任务类型
class ComputeTask {
public:
ComputeTask(int param, float factor) : data(param * factor) {}
void execute() {
// 执行计算任务
result = std::sqrt(data);
}
private:
double data;
double result;
};
// 初始化线程池(队列容量4096,8工作线程,批量大小16)
HSLL::ThreadPool<ComputeTask> pool;
if (!pool.init(4096, 8, 16)) {
throw std::runtime_error("Thread pool init failed");
}
// 提交单个任务(原位构造)
pool.emplace(100, 2.71828f);
// 批量提交参数包
struct TaskParams {
int base;
float scale;
};
TaskParams params[32];
/* 初始化参数数组... */
unsigned sent = pool.emplaceBulk(params, 32);
// 动态任务提交
auto task = ComputeTask(42, 3.14f);
pool.wait_append(std::move(task));
// 优雅关闭
pool.exit();
接口规范
核心方法
方法 | 说明 |
---|---|
bool init(queueSize, threadNum, batchSize=1) | 初始化线程池(必须先调用) |
emplace()/emplaceBulk() | 原位构造任务,避免拷贝 |
append()/appendBulk() | 提交已构造任务到队列 |
wait_* 系列 | 阻塞等待直到任务提交成功或超时 |
exit() | 停止接收新任务并等待所有任务完成 |
模板要求
- 任务类型T必须提供:
- 可调用的execute()方法
- 匹配构造函数(emplace系列使用)
- 可移动构造(append系列使用)
内部工作机制
任务处理流程
+---------------+ +----------------+ +---------------+
| Task | | BlockingQueue | | Worker Thread |
| Submitters | --> | (线程安全缓冲) | --> | (批量执行器) |
+---------------+ +----------------+ +---------------+
工作线程逻辑
void worker() {
if (batchSize == 1) {
// 单任务处理模式
while(获取任务成功) {
执行任务 -> 销毁任务
}
} else {
// 批量处理模式
T* bulkBuffer = 预分配批量内存;
while(获取批量任务成功) {
for(每个任务) {
执行任务 -> 销毁任务
}
}
}
}
注意事项
-
初始化约束:
- 必须成功调用init()后才能提交任务
- batchSize必须≥1且≤队列容量
-
生命周期管理:
- 任务对象在入队时构造,执行后立即析构
- 线程池析构时会自动调用exit()
-
线程安全:
- 所有公共方法线程安全
- 不同线程池实例之间无资源竞争
-
特殊限制:
- 任务类型禁止抛出构造/析构异常
- execute()方法不应长时间阻塞
-
资源释放:
- exit()会等待所有线程的调用返回
- 强制终止需外部干预机制
源码
#ifndef HSLL_THREADPOOL
#define HSLL_THREADPOOL
#include <vector>
#include <thread>
#include <atomic>
#include "BlockQueue.hpp"
namespace HSLL
{
/**
* @brief Thread pool class for managing worker threads and task execution
* @tparam T Type of tasks to be processed by the thread pool
*/
template <class T>
class ThreadPool
{
private:
unsigned int batchSize;
BlockQueue<T> taskQueue;
std::atomic<unsigned int> count;
std::atomic<unsigned int> error;
std::vector<std::thread> workers;
public:
ThreadPool() = default;
/**
* @brief Initialize the thread pool with specified parameters
* @param queueSize Maximum capacity of the task queue
* @param threadNum Number of worker threads to create
* @param batchSize Number of tasks to process in batch (default=1)
* @return true if initialization succeeded, false otherwise
* @note Batch size must be greater than 0 for successful initialization
*/
bool init(unsigned int queueSize, unsigned int threadNum, unsigned int batchSize = 1)
{
if (batchSize == 0)
return false;
this->batchSize = batchSize;
count = 0;
error = 0;
if (!taskQueue.init(queueSize))
return false;
for (unsigned i = 0; i < threadNum; ++i)
workers.emplace_back(&ThreadPool::worker, this);
while (count != threadNum)
std::this_thread::sleep_for(std::chrono::milliseconds(1));
if (error)
{
exit();
return false;
}
return true;
}
/**
* @brief Non-blocking element emplacement with perfect forwarding
* @tparam Args Types of arguments to forward to element constructor
* @param args Arguments to forward to element constructor
* @return true if element was emplaced, false if queue was full
* @details Constructs element in-place at the tail of the queue using
* perfect forwarding. Notifies consumers if successful.
*/
template <typename... Args>
bool emplace(Args &&...args)
{
return taskQueue.emplace(std::forward<Args>(args)...);
}
/**
* @brief Blocking element emplacement with indefinite wait
* @tparam Args Types of arguments to forward to element constructor
* @param args Arguments to forward to element constructor
* @return true if element was emplaced, false if queue was stopped
* @details Waits until queue has space or is stopped. Constructs element
* in-place and notifies consumers upon success.
*/
template <typename... Args>
bool wait_emplace(Args &&...args)
{
return taskQueue.wait_emplace(std::forward<Args>(args)...);
}
/**
* @brief Blocking element emplacement with timeout
* @tparam Rep Chrono duration representation type
* @tparam Period Chrono duration period type
* @tparam Args Types of arguments to forward to element constructor
* @param timeout Maximum duration to wait for space
* @param args Arguments to forward to element constructor
* @return true if element was emplaced, false on timeout or stop
* @details Waits up to specified duration for space. Constructs element
* in-place if space becomes available and notifies consumers.
*/
template <class Rep, class Period, typename... Args>
bool wait_emplace(const std::chrono::duration<Rep, Period> &timeout, Args &&...args)
{
return taskQueue.wait_emplace(timeout, std::forward<Args>(args)...);
}
/**
* @brief Non-blocking bulk default construction
* @param count Number of default-constructed elements to create
* @return Actual number of elements successfully created
* @details Uses TYPE's default constructor. Fails immediately if queue
* lacks sufficient space. Notifies consumers appropriately.
*/
unsigned int emplaceBulk(unsigned int count)
{
return taskQueue.emplaceBulk(count);
}
/**
* @brief Non-blocking bulk construction from parameters array
* @tparam PACKAGE Type of construction arguments for TYPE
* @param packages Pointer to array of construction arguments
* @param count Number of elements to construct
* @return Actual number of elements successfully created
* @details Constructs elements using TYPE's constructor that accepts PACKAGE.
* Copies arguments from input array. Notifies consumers with
* appropriate signal (single/multi) based on inserted quantity.
* @note TYPE must have either TYPE(PACKAGE&) or TYPE(PACKAGE) constructor
*/
template <typename PACKAGE>
unsigned int emplaceBulk(PACKAGE *packages, unsigned int count)
{
return taskQueue.emplaceBulk(packages, count);
}
/**
* @brief Blocking bulk default construction with wait
* @param count Number of default-constructed elements to create
* @return Actual number of elements created before stop/full
* @details Waits for space using TYPE's default constructor. Processes
* maximum available capacity when awakened. Notifies consumers
* based on inserted quantity.
*/
unsigned int wait_emplaceBulk(unsigned int count)
{
return taskQueue.wait_emplaceBulk(count);
}
/**
* @brief Blocking bulk construction from parameters array
* @tparam PACKAGE Type of construction arguments for TYPE
* @param packages Pointer to construction arguments array
* @param count Number of elements to construct
* @return Actual number of elements created before stop/full
* @details Waits indefinitely until space becomes available. Constructs
* elements using TYPE's constructor that accepts PACKAGE arguments.
* Returns immediately if queue is stopped.
* @note Requires TYPE(PACKAGE&) or TYPE(PACKAGE) constructor
*/
template <typename PACKAGE>
unsigned int wait_emplaceBulk(PACKAGE *packages, unsigned int count)
{
return taskQueue.wait_emplaceBulk(packages, count);
}
/**
* @brief Timed bulk default construction
* @tparam Rep Chrono duration representation type
* @tparam Period Chrono duration period type
* @param count Maximum elements to default-construct
* @param timeout Maximum wait duration
* @return Actual number of elements created
* @details Combines timed wait with default construction. Processes
* maximum possible elements if space becomes available.
* Notifies consumers based on inserted quantity.
*/
template <class Rep, class Period>
unsigned int wait_emplaceBulk(unsigned int count,
const std::chrono::duration<Rep, Period> &timeout)
{
return taskQueue.wait_emplaceBulk(count, timeout);
}
/**
* @brief Timed bulk construction from parameters array
* @tparam PACKAGE Type of construction arguments
* @tparam Rep Chrono duration representation type
* @tparam Period Chrono duration period type
* @param packages Construction arguments array
* @param count Maximum elements to construct
* @param timeout Maximum wait duration
* @return Actual number of elements constructed
* @details Waits up to timeout duration for space. Constructs elements
* using TYPE's PACKAGE-accepting constructor. Returns immediately
* on timeout or queue stop.
* @note TYPE must be constructible from PACKAGE arguments
*/
template <typename PACKAGE, class Rep, class Period>
unsigned int wait_emplaceBulk(PACKAGE *packages, unsigned int count,
const std::chrono::duration<Rep, Period> &timeout)
{
return taskQueue.wait_emplaceBulk(packages, count, timeout);
}
/**
* @brief Append a single task to the queue
* @tparam U Forward reference type for task object
* @param task Task object to be added
* @return true if task was successfully added, false otherwise
*/
template <typename U>
bool append(U &&task)
{
return taskQueue.push(std::forward<U>(task));
}
/**
* @brief Append multiple tasks to the queue in bulk
* @param tasks Pointer to array of tasks
* @param count Number of tasks in the array
* @return Number of tasks successfully added
*/
unsigned int append_bulk(T *tasks, unsigned int count)
{
return taskQueue.pushBulk(tasks, count);
}
/**
* @brief Wait and append a single task to the queue
* @tparam U Forward reference type for task object
* @param task Task object to be added
* @return true if task was successfully added before timeout, false otherwise
*/
template <typename U>
bool wait_append(U &&task)
{
return taskQueue.wait_push(std::forward<U>(task));
}
/**
* @brief Wait and append a single task with timeout
* @tparam U Forward reference type for task object
* @tparam Rep Time unit type for duration
* @tparam Period Time interval type for duration
* @param task Task object to be added
* @param timeout Maximum wait duration
* @return true if task was successfully added before timeout, false otherwise
*/
template <typename U, class Rep, class Period>
bool wait_append(U &&task, const std::chrono::duration<Rep, Period> &timeout)
{
return taskQueue.wait_push(std::forward<U>(task), timeout);
}
/**
* @brief Wait and append multiple tasks in bulk
* @param tasks Pointer to array of tasks
* @param count Number of tasks in the array
* @return Number of tasks successfully added
*/
unsigned int wait_appendBulk(T *tasks, unsigned int count)
{
return taskQueue.wait_pushBulk(tasks, count);
}
/**
* @brief Wait and append multiple tasks in bulk with timeout
* @tparam Rep Time unit type for duration
* @tparam Period Time interval type for duration
* @param tasks Pointer to array of tasks
* @param count Number of tasks in the array
* @param timeout Maximum wait duration
* @return Number of tasks successfully added before timeout
*/
template <class Rep, class Period>
unsigned int wait_appendBulk(T *tasks, unsigned int count,
const std::chrono::duration<Rep, Period> &timeout)
{
return taskQueue.wait_pushBulk(tasks, count, timeout);
}
/**
* @brief Worker thread processing function
* @note Handles both single task and bulk processing modes based on batchSize
*/
void worker()
{
if (batchSize == 1)
{
std::aligned_storage_t<sizeof(T), alignof(T)> taskBuffer;
T *taskPtr = (T *)(&taskBuffer);
count++;
while (true)
{
if (taskQueue.wait_pop(*taskPtr))
{
taskPtr->execute();
conditional_destroy(*taskPtr);
}
else
{
break;
}
}
}
else
{
T *tasks = (T *)(operator new[](batchSize * sizeof(T), std::nothrow));
if (!tasks)
error.fetch_and(1);
count++;
while (true)
{
count = taskQueue.wait_popBulk(tasks, batchSize);
if (count == 0)
break;
for (unsigned int i = 0; i < count; ++i)
{
tasks[i].execute();
conditional_destroy(tasks[i]);
}
}
operator delete[](tasks);
}
}
/**
* @brief Stop all worker threads and clean up resources
*/
void exit()
{
taskQueue.stopWait();
for (auto &th : workers)
{
if (th.joinable())
th.join();
}
taskQueue.release();
}
~ThreadPool()
{
exit();
}
ThreadPool(const ThreadPool &) = delete; ///< Disable copy constructor
ThreadPool &operator=(const ThreadPool &) = delete; ///< Disable assignment operator
};
}
#endif