C++03线程池:基于POSIX线程库的实现
什么是线程池?
线程池是一种并发编程模式,它预先创建一定数量的工作线程,这些线程会循环等待并执行提交的任务。这种模式避免了频繁创建和销毁线程带来的性能开销,是处理大量并发任务的高效解决方案。
在C++11标准之前,C++语言本身并没有提供线程库,因此线程池的实现需要依赖操作系统提供的线程接口。本文将基于POSIX线程库(pthread)实现一个兼容C++03标准的线程池。
线程池的核心优势
-
减少线程创建销毁开销:线程的创建和销毁涉及内核态操作,成本较高。线程池通过复用已创建的线程,显著减少这些操作。
-
控制资源消耗:限制并发线程的最大数量,避免因创建过多线程导致的系统资源耗尽和调度开销增加。
-
提高任务响应速度:线程预先创建并处于就绪状态,任务提交后可立即执行,无需等待线程创建。
-
简化并发编程:封装了线程管理和同步细节,开发者只需关注任务逻辑。
线程池的核心组件
一个典型的线程池包含以下核心组件:
- 工作线程:预先创建的线程,负责从任务队列中获取并执行任务
- 任务队列:存储待执行任务的数据结构,通常为先进先出(FIFO)队列
- 同步机制:确保多线程安全访问任务队列的互斥锁和条件变量
- 线程管理接口:用于创建线程池、提交任务、调整线程数量和销毁线程池
C++03线程池实现
头文件(thread_pool.h)
#ifndef THREAD_POOL_H
#define THREAD_POOL_H
#include <vector>
#include <queue>
#include <pthread.h>
#include <cstddef>
#include <stdexcept>
#include <cassert>
// 任务基类,用于类型擦除
class Task {
public:
virtual ~Task() {}
virtual void execute() = 0;
};
// 具体任务模板类
template<typename Result>
class ResultTask : public Task {
public:
// 函数指针类型定义
typedef Result (*Function)();
ResultTask(Function func) : func_(func), completed_(false) {}
void execute() {
result_ = func_();
completed_ = true;
}
Result get_result() {
// 简单的忙等待(实际应用可考虑添加超时机制)
while (!completed_) {
// 让出CPU时间片
sched_yield();
}
return result_;
}
private:
Function func_; // 任务函数
Result result_; // 任务结果
bool completed_; // 完成标志
};
// 无返回值任务特化
template<>
class ResultTask<void> : public Task {
public:
typedef void (*Function)();
ResultTask(Function func) : func_(func), completed_(false) {}
void execute() {
func_();
completed_ = true;
}
void get_result() {
while (!completed_) {
sched_yield();
}
}
private:
Function func_;
bool completed_;
};
// 线程池类
class ThreadPool {
public:
// 构造函数:指定线程数量
explicit ThreadPool(size_t num_threads)
: stop_(false), num_threads_(num_threads) {
// 初始化互斥锁
if (pthread_mutex_init(&mutex_, NULL) != 0) {
throw std::runtime_error("Failed to initialize mutex");
}
// 初始化条件变量
if (pthread_cond_init(&cond_, NULL) != 0) {
pthread_mutex_destroy(&mutex_);
throw std::runtime_error("Failed to initialize condition variable");
}
// 创建工作线程
threads_.resize(num_threads_);
for (size_t i = 0; i < num_threads_; ++i) {
if (pthread_create(&threads_[i], NULL, worker_thread, this) != 0) {
// 清理已创建的线程
stop_ = true;
pthread_cond_broadcast(&cond_);
for (size_t j = 0; j < i; ++j) {
pthread_join(threads_[j], NULL);
}
pthread_mutex_destroy(&mutex_);
pthread_cond_destroy(&cond_);
throw std::runtime_error("Failed to create thread");
}
}
}
// 析构函数
~ThreadPool() {
// 停止所有线程
pthread_mutex_lock(&mutex_);
stop_ = true;
pthread_cond_broadcast(&cond_); // 唤醒所有等待的线程
pthread_mutex_unlock(&mutex_);
// 等待所有线程结束
for (size_t i = 0; i < threads_.size(); ++i) {
pthread_join(threads_[i], NULL);
}
// 清理剩余任务
while (!tasks_.empty()) {
Task* task = tasks_.front();
tasks_.pop();
delete task;
}
// 销毁同步对象
pthread_mutex_destroy(&mutex_);
pthread_cond_destroy(&cond_);
}
// 禁止拷贝
ThreadPool(const ThreadPool&) = delete;
ThreadPool& operator=(const ThreadPool&) = delete;
// 提交有返回值的任务
template<typename Result>
Result submit(Result (*func)()) {
ResultTask<Result>* task = new ResultTask<Result>(func);
pthread_mutex_lock(&mutex_);
tasks_.push(task);
pthread_cond_signal(&cond_); // 唤醒一个工作线程
pthread_mutex_unlock(&mutex_);
Result result = task->get_result();
delete task;
return result;
}
// 提交无返回值的任务
void submit(void (*func)()) {
ResultTask<void>* task = new ResultTask<void>(func);
pthread_mutex_lock(&mutex_);
tasks_.push(task);
pthread_cond_signal(&cond_);
pthread_mutex_unlock(&mutex_);
task->get_result(); // 等待任务完成
delete task;
}
// 动态调整线程数量
void resize(size_t new_num_threads) {
if (new_num_threads == num_threads_) return;
pthread_mutex_lock(&mutex_);
size_t old_num = num_threads_;
num_threads_ = new_num_threads;
// 增加线程
if (new_num_threads > old_num) {
threads_.resize(new_num_threads);
for (size_t i = old_num; i < new_num_threads; ++i) {
if (pthread_create(&threads_[i], NULL, worker_thread, this) != 0) {
num_threads_ = i;
pthread_mutex_unlock(&mutex_);
throw std::runtime_error("Failed to resize thread pool");
}
}
}
pthread_mutex_unlock(&mutex_);
// 减少线程
if (new_num_threads < old_num) {
// 唤醒足够的线程让它们退出
for (size_t i = new_num_threads; i < old_num; ++i) {
pthread_cond_signal(&cond_);
}
// 等待多余的线程退出
for (size_t i = new_num_threads; i < old_num; ++i) {
pthread_join(threads_[i], NULL);
}
pthread_mutex_lock(&mutex_);
threads_.resize(new_num_threads);
pthread_mutex_unlock(&mutex_);
}
}
// 获取当前线程数量
size_t thread_count() const {
return num_threads_;
}
private:
// 工作线程函数(静态成员函数作为pthread入口)
static void* worker_thread(void* arg) {
ThreadPool* pool = static_cast<ThreadPool*>(arg);
pool->process_tasks();
return NULL;
}
// 线程处理任务的主循环
void process_tasks() {
while (true) {
pthread_mutex_lock(&mutex_);
// 等待任务或停止信号
while (!stop_ && tasks_.empty()) {
pthread_cond_wait(&cond_, &mutex_);
}
// 检查是否需要退出
if (stop_ || threads_.size() > num_threads_) {
pthread_mutex_unlock(&mutex_);
break;
}
// 取出任务
Task* task = tasks_.front();
tasks_.pop();
pthread_mutex_unlock(&mutex_);
// 执行任务
try {
task->execute();
} catch (const std::exception& e) {
// 简单处理异常,实际应用可根据需要扩展
fprintf(stderr, "Task execution error: %s\n", e.what());
} catch (...) {
fprintf(stderr, "Unknown task execution error\n");
}
}
}
std::vector<pthread_t> threads_; // 工作线程数组
std::queue<Task*> tasks_; // 任务队列
pthread_mutex_t mutex_; // 保护任务队列的互斥锁
pthread_cond_t cond_; // 条件变量,用于通知线程有新任务
bool stop_; // 线程池停止标志
size_t num_threads_; // 当前线程数量
};
#endif // THREAD_POOL_H
使用示例(main.cpp)
#include "thread_pool.h"
#include <iostream>
#include <cstdio>
#include <ctime>
#include <unistd.h>
// 示例任务1:计算平方
int calculate_square(int x) {
// 模拟耗时操作
usleep(10000); // 10毫秒
return x * x;
}
// 示例任务2:打印消息
void print_message(const char* msg) {
usleep(5000); // 5毫秒
printf("Message: %s\n", msg);
}
// 包装函数,用于适配线程池的函数指针要求
int square_wrapper() {
static int x = 0;
int result = calculate_square(x);
x = (x + 1) % 10; // 循环计算0-9的平方
return result;
}
// 打印消息的包装函数
void message_wrapper() {
static int count = 0;
char msg[128];
sprintf(msg, "Task %d completed", count++);
print_message(msg);
}
int main() {
try {
// 创建包含4个线程的线程池
ThreadPool pool(4);
printf("Initial thread count: %zu\n", pool.thread_count());
// 提交10个计算平方的任务
printf("\nCalculating squares:\n");
for (int i = 0; i < 10; ++i) {
int result = pool.submit(square_wrapper);
printf("%d ", result);
}
printf("\n");
// 调整线程数量为6
pool.resize(6);
printf("\nResized thread count: %zu\n", pool.thread_count());
// 提交一批打印任务
printf("\nPrinting messages:\n");
for (int i = 0; i < 8; ++i) {
pool.submit(message_wrapper);
}
// 再次调整线程数量为2
pool.resize(2);
printf("\nFinal thread count: %zu\n", pool.thread_count());
} catch (const std::exception& e) {
fprintf(stderr, "Error: %s\n", e.what());
return 1;
}
return 0;
}
线程池工作原理
-
初始化阶段:
- 创建指定数量的工作线程
- 初始化互斥锁和条件变量用于同步
- 工作线程进入等待状态,等待新任务
-
任务提交阶段:
- 将任务封装成
Task
对象 - 加锁后将任务放入任务队列
- 发送信号唤醒一个等待的工作线程
- 将任务封装成
-
任务执行阶段:
- 工作线程被唤醒,从队列中取出任务
- 解锁后执行任务
- 任务完成后,线程回到等待状态
-
动态调整阶段:
- 增加线程:直接创建新的工作线程
- 减少线程:唤醒多余线程并让它们退出
-
销毁阶段:
- 设置停止标志并唤醒所有线程
- 等待所有线程完成当前任务并退出
- 清理剩余任务和同步对象
适用场景
- 服务器开发:处理大量并发请求(如Web服务器、数据库服务器)
- 批量数据处理:图像转换、数据分析等可并行的计算任务
- 异步IO操作:网络请求、文件读写等IO密集型任务
- 实时系统:需要控制线程数量和资源使用的嵌入式系统
编译与使用
使用GCC编译时需要指定C++03标准并链接pthread库:
g++ -std=c++03 main.cpp -o thread_pool -lpthread
这个实现虽然没有C++11及以上版本的线程池简洁,但在不支持现代C++标准的环境中仍然是一个可靠的多线程任务处理方案。