线程池的设计与实现 C && C++17
常见的池式结构:线程池、连接池、内存池、对象池、协程池、线程池
常见的“池式结构”
池类型 | 描述 | 应用场景 |
---|---|---|
线程池(Thread Pool) | 复用固定数量的线程来处理大量任务 | 并发服务器、任务调度系统 |
连接池(Connection Pool) | 复用数据库连接、HTTP连接等资源 | Web服务访问数据库/缓存时 |
内存池(Memory Pool) | 提前申请内存块,避免频繁 malloc/free | 游戏开发、网络缓冲区 |
对象池(Object Pool) | 管理某一类对象的生命周期(通常带初始化成本) | 图形对象、数据库实体、Socket |
协程池(Coroutine Pool) | 循环复用一定数量的协程结构 | 高并发服务端、异步框架 |
线程本地池(Thread-Local Pool) | 每个线程拥有自己的对象池/内存池 | 避免加锁、提升并发性能 |
为什么要用池式结构?
- 避免频繁创建/销毁资源
- 比如创建线程、建立数据库连接、分配大内存都是昂贵操作;
- 池化后,这些资源只创建一次,多次使用,避免频繁系统调用。
- 资源复用,降低系统开销
- 避免了每次新建资源的时间和内存分配;
- 减少 CPU 调度、内存碎片,提高缓存命中率。
- 集中管理资源,便于控制和限制
- 池可以统一限制资源数量,防止资源耗尽(如连接数、线程数);
- 管理起来更有序,也方便监控和调优。
- 提升性能和响应速度
- 比如请求到来时,直接从池中取出资源,几乎无等待;
- 尤其在高并发场景,池化技术是提升 QPS 的重要手段。
线程池
线程池是预先创建好的一组线程,在执行任务的时候复用这组线程,可以降低系统创建和销毁线程带来的开销。
带来的好处就是:
- 不会占用核心线程,异步执行耗时任务。(系统不会被重计算任务、IO操作阻塞)
- 并发,提高系统的性能
为什么需要线程池:
如果某类任务特别耗时,那么他会严重影响该线程处理其他任务(包括重计算任务,IO任务),解决方式就是将这类任务放到其他线程执行,异步执行需要上下文和执行函数,从而达到充分利用系统资源、复用现场资源、异步执行耗时任务的作用
线程池的构成:
生产者线程:负责发布耗时任务;
任务队列:内部包含任务(上下文和回调函数)
消费者线程(线程池):负责取出任务,执行任务,线程调度
如何实现线程池:
数据结构设计
任务:
typedef void (*handler_pt)(void * /* ctx */); //具体执行函数
//任务节点
struct task_s {
void *next;
handler_pt func;
void *arg;
} task_t;
任务队列
typedef struct task_queue_s {
void *head; //头指针
void **tail; //尾指针
int block;
spinlock_t lock; //自旋锁,用于等待时间短的场景,避免调度开销
pthread_mutex_t mutex; //互斥锁,用于操作队列,耗时操作使用互斥锁
pthread_cond_t cond; //条件变量
} task_queue_t;
线程池
struct thrdpool_s {
task_queue_t *task_queue; //任务队列
atomic_int quit; //退出标志位
int thrd_count; //线程数量
pthread_t *threads; //真正的线程池
};
接口设计
创建队列
static task_queue_t *
__taskqueue_create() {
int ret;
task_queue_t *queue = (task_queue_t *)malloc(sizeof(task_queue_t));
if (queue) {
ret = pthread_mutex_init(&queue->mutex, NULL);
if (ret == 0) {
ret = pthread_cond_init(&queue->cond, NULL);
if (ret == 0) {
spinlock_init(&queue->lock);
queue->head = NULL;
queue->tail = &queue->head;
queue->block = 1;
return queue;
}
pthread_mutex_destroy(&queue->mutex);
}
free(queue);
}
return NULL;
}
设置非阻塞
static void
__nonblock(task_queue_t *queue) {
pthread_mutex_lock(&queue->mutex);
queue->block = 0;
pthread_mutex_unlock(&queue->mutex);
pthread_cond_broadcast(&queue->cond);
}
添加任务
static inline void
__add_task(task_queue_t *queue, void *task) {
// 不限定任务类型,只要该任务的结构起始内存是一个用于链接下一个节点的指针
void **link = (void**)task; //转换成二级指针后,link起始地址为task的起始地址,长度为8
*link = NULL; //link 解引用后存放NULL
spinlock_lock(&queue->lock); //这里使用自旋锁,因为队列出入操作是不耗时的
*queue->tail = link; //这里对queue的tail解引用,相当于是task的前八个字节,也就是next成员,等价于 queue->tail->next,尾插
queue->tail = link; //将节点插入队列的尾部
spinlock_unlock(&queue->lock);
pthread_cond_signal(&queue->cond);
}
弹出任务
static inline void *
__pop_task(task_queue_t *queue) {
spinlock_lock(&queue->lock);
if (queue->head == NULL) {
spinlock_unlock(&queue->lock);
return NULL;
}
task_t *task;
task = queue->head;
void **link = (void**)task;
queue->head = *link;
if (queue->head == NULL) {
queue->tail = &queue->head;
}
spinlock_unlock(&queue->lock);
return task;
}
获取任务
static inline void *
__get_task(task_queue_t *queue) {
task_t *task;
// 虚假唤醒
while ((task = __pop_task(queue)) == NULL) { //Linux建议开发者自己处理虚假唤醒的情况
pthread_mutex_lock(&queue->mutex); //这里使用互斥锁,因为是耗时操作
if (queue->block == 0) {
pthread_mutex_unlock(&queue->mutex);
return NULL;
}
// 1. 先 unlock(&mtx)
// 2. 在 cond 休眠
// --- __add_task 时唤醒
// 3. 在 cond 唤醒
// 4. 加上 lock(&mtx);
pthread_cond_wait(&queue->cond, &queue->mutex); //条件等待的时候会解锁,唤醒后重新枷锁
pthread_mutex_unlock(&queue->mutex);
}
return task;
}
销毁队列
static void
__taskqueue_destroy(task_queue_t *queue) {
task_t *task;
while ((task = __pop_task(queue))) {
free(task);
}
spinlock_destroy(&queue->lock);
pthread_cond_destroy(&queue->cond);
pthread_mutex_destroy(&queue->mutex);
free(queue);
}
工作函数
static void *
__thrdpool_worker(void *arg) {
thrdpool_t *pool = (thrdpool_t*) arg;
task_t *task;
void *ctx;
while (atomic_load(&pool->quit) == 0) {
task = (task_t*)__get_task(pool->task_queue);
if (!task) break;
handler_pt func = task->func;
ctx = task->arg;
free(task);
func(ctx);
}
return NULL;
}
结束线程
static void
__threads_terminate(thrdpool_t * pool) {
atomic_store(&pool->quit, 1);
__nonblock(pool->task_queue);
int i;
for (i=0; i<pool->thrd_count; i++) {
pthread_join(pool->threads[i], NULL);
}
}
创建线程
static int
__threads_create(thrdpool_t *pool, size_t thrd_count) {
pthread_attr_t attr;
int ret;
ret = pthread_attr_init(&attr);
if (ret == 0) {
pool->threads = (pthread_t *)malloc(sizeof(pthread_t) * thrd_count);
if (pool->threads) {
int i = 0;
for (; i < thrd_count; i++) {
if (pthread_create(&pool->threads[i], &attr, __thrdpool_worker, pool) != 0) {
break;
}
}
pool->thrd_count = i;
pthread_attr_destroy(&attr);
if (i == thrd_count){
return 0; //确认创建的线程数量,相等才返回
}
__threads_terminate(pool); //不相等处理逻辑
free(pool->threads);
}
ret = -1;
}
return ret;
}
用户接口
结束线程池
void
thrdpool_terminate(thrdpool_t * pool) {
atomic_store(&pool->quit, 1);
__nonblock(pool->task_queue);
}
创建线程池
thrdpool_t *
thrdpool_create(int thrd_count) {
thrdpool_t *pool;
pool = (thrdpool_t*)malloc(sizeof(*pool));
if (pool) {
task_queue_t *queue = __taskqueue_create();
if (queue) {
pool->task_queue = queue;
atomic_init(&pool->quit, 0);
if (__threads_create(pool, thrd_count) == 0)
return pool;
__taskqueue_destroy(queue);
}
free(pool);
}
return NULL;
}
往线程池添加任务
int
thrdpool_post(thrdpool_t *pool, handler_pt func, void *arg) {
if (atomic_load(&pool->quit) == 1)
return -1;
task_t *task = (task_t*) malloc(sizeof(task_t));
if (!task) return -1;
task->func = func;
task->arg = arg;
__add_task(pool->task_queue, task);
return 0;
}
等待任务结束
void
thrdpool_waitdone(thrdpool_t *pool) {
int i;
for (i=0; i<pool->thrd_count; i++) {
pthread_join(pool->threads[i], NULL);
}
__taskqueue_destroy(pool->task_queue);
free(pool->threads);
free(pool);
}
使用示例:
int done = 0;
pthread_mutex_t lock;
void do_task(void *arg) {
thrdpool_t *pool = (thrdpool_t*)arg;
pthread_mutex_lock(&lock);
done++;
printf("doing %d task\n", done);
pthread_mutex_unlock(&lock);
if (done >= 1000) {
thrdpool_terminate(pool);
}
}
void test_thrdpool_basic() {
int threads = 8;
pthread_mutex_init(&lock, NULL);
thrdpool_t *pool = thrdpool_create(threads);
if (pool == NULL) {
perror("thread pool create error!\n");
exit(-1);
}
while (thrdpool_post(pool, &do_task, pool) == 0) {
}
thrdpool_waitdone(pool);
pthread_mutex_destroy(&lock);
}
int main(int argc, char **argv) {
test_thrdpool_basic();
return 0;
}
C语言版本适合理解原理,使用C++进行封装,可以跨平台,同时也更精简
#pragma once
#include <vector>
#include <queue>
#include <thread>
#include <mutex>
#include <shared_mutex>
#include <condition_variable>
#include <functional>
#include <future>
#include <atomic>
#include <chrono>
#include <stdexcept>
#include <type_traits>
#include <iostream>
#include <utility>
#include <optional>
class ThreadPool {
public:
explicit ThreadPool(size_t num_threads);
~ThreadPool();
// 提交任务,返回 future
//使用C++17,支持函数、Lambda表达式、std::function、成员函数指针、成员变量指针
template<typename F, typename... Args>
auto submit(F&& f, Args&&... args) -> std::future<std::invoke_result_t<F, Args...>>;
// 等待所有任务执行完毕
void wait_done();
// 终止线程池
void shutdown();
private:
void worker_loop();
std::vector<std::thread> workers; //线程池的部分
std::queue<std::function<void()>> tasks; //任务队列
std::mutex queue_mutex; //互斥锁
std::condition_variable condition; //条件变量
std::atomic<bool> stop{false}; //原子的关闭标志位
std::atomic<int> active_tasks{0}; //原子的活跃任务数量
};
// 构造函数
inline ThreadPool::ThreadPool(size_t num_threads) {
for (size_t i = 0; i < num_threads; ++i)
workers.emplace_back(&ThreadPool::worker_loop, this);
}
// 工作线程主循环
inline void ThreadPool::worker_loop() {
while (true) {
std::function<void()> task;
{
std::unique_lock lock(queue_mutex);
//处理虚假唤醒
condition.wait(lock, [this] {
return stop || !tasks.empty(); //如果stop为空,或者任务队列为空,线程等待
});
if (stop && tasks.empty()) return; //如果线程池要求stop,而且没有需要执行的任务,安全退出
//取任务
task = std::move(tasks.front());
tasks.pop();
++active_tasks;
}
//执行
task();
--active_tasks;
}
}
// 提交任务
template<typename F, typename... Args>
auto ThreadPool::submit(F&& f, Args&&... args) -> std::future<std::invoke_result_t<F, Args...>> {
//返回值类型推导
using ReturnType = std::invoke_result_t<F, Args...>;
//任务封装
auto task_ptr = std::make_shared<std::packaged_task<ReturnType()>>(
std::bind(std::forward<F>(f), std::forward<Args>(args)...)
);
//获取结果
std::future<ReturnType> result = task_ptr->get_future();
{ //临界区中将任务加入队列:
std::scoped_lock lock(queue_mutex); //C++17 的统一锁
if (stop) { //如果已经要关闭线程池了,就停止提交新任务了
throw std::runtime_error("Cannot submit to stopped ThreadPool");
}
tasks.emplace([task_ptr]() {
std::invoke(*task_ptr); //std::invoke() 是 C++17 的万能函数调用器
});
}
condition.notify_one(); //唤醒工作线程
return result;
}
// 等待所有任务完成
inline void ThreadPool::wait_done() {
while (true) {
if (tasks.empty() && active_tasks.load() == 0) break;
std::this_thread::sleep_for(std::chrono::milliseconds(1)); //睡眠 1 毫秒
}
}
// 关闭线程池
inline void ThreadPool::shutdown() {
{
std::scoped_lock lock(queue_mutex);
stop = true;
}
condition.notify_all();
for (auto& thread : workers)
if (thread.joinable()) thread.join();
}
// 析构函数
inline ThreadPool::~ThreadPool() {
shutdown();
}