使用C++实现一个简单的线程池

本文详细阐述了线程池的工作原理,如何通过自定义参数控制线程数量,以及任务调度的设计细节。介绍了线程池在性能优化和代码复用中的作用。

线程池类似于内存池,连接池等,在一开始创建好一定数量的线程,当有任务需要处理时,就将该任务丢进线程池的某个线程中处理,而不是每个任务都启动一个线程。使用线程池的好处在于,不需要频繁的创建线程,因为创建线程是一个较耗资源的操作。因此,相对而言一开始创建好线程,要比动态创建线程对性能影响更小。其次,线程池可以更好的将线程管理起来,对外提供简单的接口,内部完成对线程的调度,这也是高内聚的一个思想,将复杂封装起来,对外在满足业务场景的前提下尽可能简单。线程池作为一个独立的非业务模块,任何需要处理多任务的场景都可以使用该组件,这样更有利于代码复用。

需求分析:

  1. 线程池可以根据自定义参数实现线程数量控制,比如,可以设置初始线程数量,可以设置最大线程数量,可以设置线程空闲时间。
  • 初始线程数量为线程池初始化时,创建线程的数量。此数量是线程池最小线程数,任何时候线程池都应该有这么多线程。
  • 最大线程数量为线程池支持的最大线程数量,线程数量不是越多越好,线程数过多会导致大量的线程切换,这本身又会造成性能问题,因此使用最大线程数量来限制线程数量。 一般情况下,初始线程数量小于最大线程数量,当任务数量较多时,线程池能够动态扩展线程数量。同样,线程比较空闲时,为了不占用过多的线程资源,可以将空闲线程退出。
  • 线程空闲时间,当线程池在一段时间内都没有事情可干,那么就将此线程退出。
  1. 任务调度设计,任务进入线程池,由哪个线程处理,如何平均的将任务分配给各个线程?这里需要一个队列,用来缓存任务,外部通过接口将任务推到队列,线程池内部需要快速将任务分配给某个线程。这里设计为让线程内部去竞争获得任务,这样就不需要设计任务分配相关算法,简化了设计。线程池中的各个线程,当有任务的时候从队列里面拿任务,当没有任务的时候希望线程能够挂起。使用条件变量,当没有任务的时候,将线程挂起来,当有任务时,由外部通知条件变量,唤醒线程,获取任务,执行任务。这样的设计,在同一时刻只能有一个线程拿到一个任务,这也是对任务队列的保护。在任务运行时也可以达到并行的效果,这样线程池的设计目的也就达到了。

代码实现

// ThreadPool.h
#ifndef __THREAD_POOL_H__
#define __THREAD_POOL_H__
#include <functional>
#include <queue>
#include <vector>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <atomic>
namespace threadpool
{

class ThreadPool
{
    using TaskType = std::function<void (void*)>;

    static const int INIT_NUM = 1;
    static const int MAX_NUM = 4;
    static const int MAX_IDLE_TIME_SECOND = 6;
public:
    ThreadPool(int initNum, int maxNum, int idleSec);
    ThreadPool();
    ~ThreadPool();

public:
    void AddTask(const TaskType& task, void* arg);

private:
    void Init();
    void ThreadRoutine(int index);
    void PoolGrow();
    
private:
    int _initNum;
    int _maxNum;
    int _idleSec;
    bool _stop;
    std::queue<std::pair<TaskType, void*>> _taskQueue;
    std::vector<std::thread*> _pool;
    std::mutex _mutex;
    std::condition_variable _cond;
    std::atomic<int> _busyCount;
    std::atomic<int> _threadCount;
};


} // namespace threadpool

#endif // __THREAD_POOL_H__
// ThreadPool.cpp
#include <assert.h>
#include <stdio.h>
#include "ThreadPool.h"

using namespace threadpool;

const int ThreadPool::INIT_NUM;
const int ThreadPool::MAX_NUM;
const int ThreadPool::MAX_IDLE_TIME_SECOND;

ThreadPool::ThreadPool(int initNum, int maxNum, int idleSec)
    : _initNum(initNum)
    , _maxNum(maxNum)
    , _idleSec(idleSec)
    , _stop(false)
    , _busyCount(0)
    , _threadCount(0)
{
    Init();
}

ThreadPool::ThreadPool()
    : _initNum(ThreadPool::INIT_NUM)
    , _maxNum(ThreadPool::MAX_NUM)
    , _idleSec(ThreadPool::MAX_IDLE_TIME_SECOND)
    , _stop(false)
    , _busyCount(0)
    , _threadCount(0)
{
    Init();
}


void ThreadPool::Init()
{
    if(_idleSec <= 0){
        _idleSec = ThreadPool::MAX_IDLE_TIME_SECOND;
    }
    _pool.reserve(_maxNum);
    for(int i = 0; i < _maxNum; ++i){
        if(i < _initNum){
            _pool.push_back(new std::thread(&ThreadPool::ThreadRoutine, this, i));
        }else{
            _pool.push_back(nullptr);
        }
    }

    _threadCount.store(_initNum);
}

ThreadPool::~ThreadPool()
{
    {
        std::lock_guard<std::mutex> lock(_mutex);
        _stop = true;
    }
    _cond.notify_all();
    for(int i = 0; i < _maxNum; ++i){
        auto thread = _pool[i];
        if(thread && thread->joinable()){
            thread->join();
            delete thread;
        }
    }
}


void ThreadPool::AddTask(const TaskType& task, void* arg)
{
    {
        std::lock_guard<std::mutex> lock(_mutex);
        _taskQueue.emplace(task, arg);
    }
   
    _cond.notify_one();

    PoolGrow();
}

void ThreadPool::PoolGrow()
{
    int busy = _busyCount.load();
    int threadCount = _threadCount.load();
    printf("count:%d, busy:%d\n", threadCount, busy);
    if(threadCount == busy){
        if(threadCount < _maxNum){
            for(int i = 0; i < _maxNum; ++i){
                if(_pool[i] == nullptr){
                    _pool[i] = new std::thread(&ThreadPool::ThreadRoutine, this, i);
                    printf("add thread[%d]\n", i);
                    ++_threadCount;
                    break;
                }
            }
        }
    }
}



void ThreadPool::ThreadRoutine(int index)
{
    while(1){
        std::pair<TaskType, void*> task(nullptr, 0);
        {
            std::cv_status waitStatus = std::cv_status::no_timeout;
            std::unique_lock<std::mutex> lock(_mutex);
            while(_taskQueue.empty() && waitStatus != std::cv_status::timeout && !_stop){
                int idleTime = MAX_IDLE_TIME_SECOND;
                waitStatus =  _cond.wait_for(lock, std::chrono::seconds(_idleSec));
            }
            if(!_taskQueue.empty()){
                task = _taskQueue.front();
                _taskQueue.pop();
            }else if(_stop){
                break;
            }else if(waitStatus == std::cv_status::timeout){
                if(_threadCount > _initNum){
                    _pool[index]->detach();
                    delete _pool[index];
                    _pool[index] = nullptr;
                    --_threadCount;
                    printf("thread[%d] exit\n", index);
                    break;
                }
            }
        }

        if(task.first != nullptr){
            ++_busyCount;
            task.first(task.second);
            --_busyCount;
        }
    }
}

使用示例

#include <stdio.h>
#include "ThreadPool.h"
int main()
{
	int initThreadCount = 1;    // 初始线程个数
	int maxThreadCount = 4;     // 最大线程个数
	int idleSec = 6;            // 当某个线程空闲6秒时,将退出
	threadpool::ThreadPool pool(initThreadCount, maxThreadCount, idleSec);
	
	// 向线程池中添加任务
	pool.AddTask([](void* arg){
	  printf("this is task1.\n");
	});
	
	pool.AddTask([](void* arg){
	  printf("this is task2.\n");
	});
	
	pool.AddTask([](void* arg){
	  printf("this is task3.\n");
	});
}

<think>好的,用户让我用C++和Qt实现一个线程池。首先,我需要回忆一下Qt中与多线程相关的类,比如QThread、QRunnable、QThreadPool这些。用户可能希望有一个自定义的线程池管理类,能够提交任务并管理线程的生命周期。 首先,线程池的核心是管理一组线程,这些线程可以执行提交的任务。在Qt中,QThreadPool已经是一个现成的类,但用户可能希望了解底层实现或者需要自定义功能,比如限制最大线程数、任务队列管理等。 接下来,我需要考虑如何设计线程池的结构。通常,线程池需要一个任务队列,多个工作线程从队列中取出任务执行。任务队列需要是线程安全的,所以需要使用互斥锁(QMutex)和条件变量(QWaitCondition)来同步访问。 然后,每个工作线程应该是一个QThread的子类,或者使用QRunnable。不过,使用QThread可能更直接,因为可以更灵活地控制线程的生命周期。不过,Qt的QThreadPool本身已经处理了这些,但用户可能希望从头实现。 需要定义一个线程池管理类,比如ThreadPool,它包含任务队列、线程列表、最大线程数等属性。任务队列可以用QQueue<QRunnable*>,然后每个线程不断从队列中取出任务执行。 还要考虑线程的创建和销毁。当任务队列中有任务时,如果有空闲线程或者线程数未达到最大值,就创建新线程。但可能更高效的是预先创建一定数量的线程,避免频繁创建销毁的开销。 另外,线程池需要支持异步任务提交,可能需要使用Qt的信号槽机制来通知任务完成。或者,使用QFuture和QPromise来返回结果,但可能增加复杂度。 需要处理线程的同步问题,确保多个线程安全地访问任务队列。使用QMutex锁定队列,当队列为空时,线程等待条件变量;当有新任务时,唤醒线程。 还要考虑线程池的停止机制,确保所有线程在程序结束时正确退出。可能需要一个停止标志,在析构函数中等待所有线程完成。 然后,编写代码的大致结构: 1. 定义一个WorkerThread类,继承自QThread,重写run()方法,循环从任务队列中取出任务执行。 2. ThreadPool类,管理WorkerThread的创建、销毁,维护任务队列,提供submit方法。 3. 线程安全的队列操作,使用互斥锁和条件变量。 可能遇到的问题: - 如何避免线程饥饿?确保任务公平分配。 - 如何限制最大线程数,防止资源耗尽。 - 如何处理任务队列的饱和策略(拒绝策略)。 - 线程池的生命周期管理,确保在析构时所有线程正确退出。 需要测试线程池的功能,比如提交多个任务,检查是否并发执行,线程数是否正确控制,以及停止时的行为。 最后,可能需要提供使用示例,比如继承QRunnable的自定义任务类,并提交到线程池中执行。 现在,将这些思考转化为结构清晰的代码和说明,确保涵盖关键点,如线程安全、任务队列管理、线程创建和销毁,以及使用示例。</think>以下是使用 C++ 和 Qt 实现线程池方案,包含完整类设计和使用示例: --- ### **1. 线程池核心类设计** ```cpp // ThreadPool.h #pragma once #include <QObject> #include <QThread> #include <QQueue> #include <QMutex> #include <QWaitCondition> #include <QRunnable> class ThreadPool : public QObject { Q_OBJECT public: static ThreadPool& getInstance() { static ThreadPool instance; return instance; } void start(QRunnable* task); void stop(); void setMaxThreadCount(int max); int activeThreadCount() const; signals: void started(); void finished(); private: explicit ThreadPool(QObject* parent = nullptr); ~ThreadPool(); class WorkerThread; QQueue<QRunnable*> m_taskQueue; QList<WorkerThread*> m_workers; QMutex m_queueMutex; QWaitCondition m_taskAvailable; int m_maxThreads = QThread::idealThreadCount(); bool m_stopped = false; }; ``` --- ### **2. 工作线程实现** ```cpp // ThreadPool.cpp #include "ThreadPool.h" class ThreadPool::WorkerThread : public QThread { public: explicit WorkerThread(ThreadPool* pool) : m_pool(pool) {} void run() override { forever { QMutexLocker locker(&m_pool->m_queueMutex); if (m_pool->m_taskQueue.isEmpty() && !m_pool->m_stopped) { m_pool->m_taskAvailable.wait(&m_pool->m_queueMutex); continue; } if (m_pool->m_stopped) break; auto task = m_pool->m_taskQueue.dequeue(); locker.unlock(); emit m_pool->started(); task->run(); emit m_pool->finished(); } } private: ThreadPool* m_pool; }; ``` --- ### **3. 线程池方法实现** ```cpp ThreadPool::ThreadPool(QObject* parent) : QObject(parent) { // 初始化默认线程数 setMaxThreadCount(m_maxThreads); } ThreadPool::~ThreadPool() { stop(); for (auto worker : m_workers) { worker->wait(); delete worker; } } void ThreadPool::setMaxThreadCount(int max) { m_maxThreads = qMax(1, max); while (m_workers.size() < m_maxThreads) { auto worker = new WorkerThread(this); worker->setObjectName(QString("Worker-%1").arg(m_workers.size()+1)); m_workers.append(worker); worker->start(); } } void ThreadPool::start(QRunnable* task) { QMutexLocker locker(&m_queueMutex); if (m_stopped) return; m_taskQueue.enqueue(task); m_taskAvailable.wakeOne(); } void ThreadPool::stop() { QMutexLocker locker(&m_queueMutex); m_stopped = true; m_taskAvailable.wakeAll(); } int ThreadPool::activeThreadCount() const { return m_workers.size() - m_taskQueue.size(); } ``` --- ### **4. 使用示例** ```cpp // 自定义任务类 class MyTask : public QRunnable { public: void run() override { qDebug() << "Executing task in thread" << QThread::currentThreadId(); // 模拟耗时操作 QThread::msleep(500); } }; // 主程序使用 #include "ThreadPool.h" #include <QApplication> int main(int argc, char* argv[]) { QApplication app(argc, argv); // 获取线程池实例 auto& pool = ThreadPool::getInstance(); pool.setMaxThreadCount(4); // 设置最大线程数 // 提交10个任务 for (int i = 0; i < 10; ++i) { pool.start(new MyTask()); } return app.exec(); } ``` --- ### **5. 关键特性说明** 1. **单例模式** 通过 `getInstance()` 提供全局访问点,确保整个应用使用一个线程池 2. **动态线程管理** - 初始线程数自动适配 CPU 核心数 - 可通过 `setMaxThreadCount()` 动态调整最大线程数 3. **线程安全机制** - 使用 `QMutex` 保护任务队列 - 使用 `QWaitCondition` 实现线程阻塞唤醒机制 4. **优雅停止** - `stop()` 方法会安全终止所有线程 - 析构函数确保清理所有资源 5. **信号通知** - 提供 `started()` 和 `finished()` 信号用于监控任务状态 --- ### **相关问题** 1. 如何实现任务优先级调度? 2. 如何为线程池添加超时任务处理机制? 3. 如何将线程池与 QML 结合使用? 4. 如何实现线程池的动态负载均衡? 5. 如何在任务间传递上下文数据?
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值