线程池的设计与实现 C && C++17

常见的池式结构:线程池、连接池、内存池、对象池、协程池、线程池

常见的“池式结构”

池类型 描述 应用场景
线程池(Thread Pool) 复用固定数量的线程来处理大量任务 并发服务器、任务调度系统
连接池(Connection Pool) 复用数据库连接、HTTP连接等资源 Web服务访问数据库/缓存时
内存池(Memory Pool) 提前申请内存块,避免频繁 malloc/free 游戏开发、网络缓冲区
对象池(Object Pool) 管理某一类对象的生命周期(通常带初始化成本) 图形对象、数据库实体、Socket
协程池(Coroutine Pool) 循环复用一定数量的协程结构 高并发服务端、异步框架
线程本地池(Thread-Local Pool) 每个线程拥有自己的对象池/内存池 避免加锁、提升并发性能

为什么要用池式结构?

  1. 避免频繁创建/销毁资源
  • 比如创建线程、建立数据库连接、分配大内存都是昂贵操作;
  • 池化后,这些资源只创建一次,多次使用,避免频繁系统调用。
  1. 资源复用,降低系统开销
  • 避免了每次新建资源的时间和内存分配;
  • 减少 CPU 调度、内存碎片,提高缓存命中率。
  1. 集中管理资源,便于控制和限制
  • 池可以统一限制资源数量,防止资源耗尽(如连接数、线程数);
  • 管理起来更有序,也方便监控和调优。
  1. 提升性能和响应速度
  • 比如请求到来时,直接从池中取出资源,几乎无等待;
  • 尤其在高并发场景,池化技术是提升 QPS 的重要手段。

线程池

线程池是预先创建好的一组线程,在执行任务的时候复用这组线程,可以降低系统创建和销毁线程带来的开销。

带来的好处就是:

  1. 不会占用核心线程,异步执行耗时任务。(系统不会被重计算任务、IO操作阻塞)
  2. 并发,提高系统的性能

为什么需要线程池:

​ 如果某类任务特别耗时,那么他会严重影响该线程处理其他任务(包括重计算任务,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();
}

posted @ 2025-04-01 11:16  Tohomson  阅读(392)  评论(0)    收藏  举报