线程池的简单理解

线程池深入解析

​📒个人主页:热爱生活的李📒
​❤️感谢大家阅读本文,同时欢迎访问本人主页查看更多文章​❤️
🙏本人也在学习阶段,如若发现问题,请告知,非常感谢🙏

ThreadPoolExecutor

线程池状态

ThreadPoolExecutor使用int的高三位来表示线程池状态,低29位表示线程数量

状态名高三位接收新任务处理阻塞队列任务说明
RUNNING111YY
SHUTDOWN000NY不会接收新任务,但处理队列中剩余的队伍
STOP001NN会中断正在执行的任务,并抛弃处理队列中任务
TIDYING010任务全执行完毕,活动线程为0即将进入终结
TERMINATED终结状态

构造方法

参数名参数含义
corePoolSize核心线程数目
maximumPoolSize最大线程数目
keepAliveTime生存时间-针对救急线程
unit时间单位-针对救急线程
workQueue阻塞队列
threadFactory线程工厂
handler拒绝队列

1、当线程池中刚开始是没有线程的,当一个任务提交给线程池后,线程池才会创建一个新线程来执行任务
2、当线程池达到corePoolSize并没有线程空闲时候,这时再加入任务,新加的任务会被加入到workQueue队列排队,直到有空闲线程
3、如果有界队列满了且没有空闲线程,就会创建maximumPoolSize-corePoolSize数目的救急线程
4、如果线程达到了maximumPoolSize仍然有新任务这时就会执行拒绝策略
抛出RejectedExecutionException异常,这是默认策略
CallerRunPolicy让调用者运行任务
DiscardPolicy放弃本次任务
DiscardOldestPolicy放弃队列中最早的任务
5、当高峰过去后,救急线程如果一段时间没有任务做,需要结束节省资源

newFixedThreadPool

特点
核心线程数==最大线程数,没有救急线程
阻塞队列是无界的,可以放任意数量的任务
使用于任务量已知,相对耗时的任务

newCachedThreadPool

特点
核心线程数是0,最大线程数是Integer。MAX_VALUE,救急线程的生存空间的60s
队列采用SynchronousQueue实现特点是,它没有容量,没有线程来取是放不进去的
整个线程池表现为线程池数会根据任务量不断增长,没有增长,当任务执行完毕,空闲1分钟后释放线程
适合任务数比较密集,但每个任务执行时间较短的情况

newSingleThreadExecutor
使用场景
希望多个任务排队执行。线程数固定为1,任务数多于1时会放入无界队列排队,任务执行完毕,这唯一的线程也不会被释放
区别
自己创建一个单线程串行执行任务,如果任务执行失败而终止那么没有补救条件,而线程池还会再创建一个线程,保证池的正常操作

newScheduledThreadPool

执行方法

void execute(Runnable command);
执行任务

Future submit(Callable task)
提交任务task,用返回值Future获取任务执行结果

List<Future> invokeAll(Collection<? extends Callable> tasks)
提交tasks中所有的任务

List<Future> invokeAll(Collection<? extends Callable> tasks, long timeout, TimeUnit unit)
提交tasks中所有的任务带超时时间

T invokeAny(Collection<? extends Callable> tasks)
提交tasks中的任务那个先执行完毕返回该任务的结果其它任务取消

T invokeAny(Collection<? extends Callable> tasks,long timeout, TimeUnit unit)
提交tasks中的任务那个先执行完毕返回该任务的结果其它任务取消带超时时间

关闭线程池

shutdown()
线程池状态修改为SHUTDOWN
不会接收新的任务
但已提交的会执行完
只打断空闲线程
此方法不会阻塞调用线程的执行

    public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            //修改线程池状态
            advanceRunState(SHUTDOWN);
            //打断空闲线程
            interruptIdleWorkers();
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        //尝试终结
        tryTerminate();
    }

shutdownNow
修改线程池状态为stop
不接收新任务
打断所有线程
会将队列中的任务返回

    public List<Runnable> shutdownNow() {
        List<Runnable> tasks;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            //修改线程池状态
            advanceRunState(STOP);
            //打断所有线程
            interruptWorkers();
            //获取队列这种剩余任务
            tasks = drainQueue();
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
        return tasks;
    }
<think>我们正在讨论C++11线程池的实现和实际应用。用户希望理解并实现一个基于C++11标准的线程池,并进行相关开发实战。 首先,我们需要明确线程池的基本概念:线程池是一种多线程处理形式,它维护多个线程等待任务分配,避免频繁创建和销毁线程的开销。在C++11中,我们可以使用`<thread>`, `<mutex>`, `<condition_variable>`, `<future>`, `<functional>`, `<queue>`等标准库组件来实现。 下面是一个简单线程池实现步骤: 1. 创建一个任务队列,用于存放待执行的任务(函数对象)。 2. 创建一组工作线程,这些线程不断从任务队列中取出任务并执行。 3. 使用互斥锁(mutex)和条件变量(condition_variable)来同步任务队列的访问。 4. 提供向任务队列中添加任务的接口,通常使用`std::function`和`std::future`来返回任务执行结果。 5. 在析构函数中停止所有线程(通常通过设置标志位和通知所有线程)。 下面是一个基本的线程池实现示例: ```cpp #include <vector> #include <thread> #include <mutex> #include <condition_variable> #include <queue> #include <functional> #include <future> class ThreadPool { public: ThreadPool(size_t num_threads); ~ThreadPool(); // 添加任务到队列,返回一个future以获取结果 template<class F, class... Args> auto enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type>; private: std::vector<std::thread> workers; std::queue<std::function<void()>> tasks; std::mutex queue_mutex; std::condition_variable condition; bool stop; }; // 构造函数,启动指定数量的工作线程 ThreadPool::ThreadPool(size_t num_threads) : stop(false) { for (size_t i = 0; i < num_threads; ++i) { workers.emplace_back([this] { while (true) { std::function<void()> task; { std::unique_lock<std::mutex> lock(this->queue_mutex); this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); }); if (this->stop && this->tasks.empty()) return; task = std::move(this->tasks.front()); this->tasks.pop(); } task(); } }); } } // 析构函数,停止所有线程 ThreadPool::~ThreadPool() { { std::unique_lock<std::mutex> lock(queue_mutex); stop = true; } condition.notify_all(); for (std::thread &worker : workers) worker.join(); } // 添加任务到队列 template<class F, class... Args> auto ThreadPool::enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> { using return_type = typename std::result_of<F(Args...)>::type; auto task = std::make_shared< std::packaged_task<return_type()> >( std::bind(std::forward<F>(f), std::forward<Args>(args)...) ); std::future<return_type> res = task->get_future(); { std::unique_lock<std::mutex> lock(queue_mutex); if (stop) throw std::runtime_error("enqueue on stopped ThreadPool"); tasks.emplace([task](){ (*task)(); }); } condition.notify_one(); return res; } ``` ### 使用示例 ```cpp #include <iostream> #include <chrono> int main() { ThreadPool pool(4); // 创建4个线程的线程池 // 添加一些任务 auto result1 = pool.enqueue([](int a, int b) { std::this_thread::sleep_for(std::chrono::seconds(1)); return a + b; }, 1, 2); auto result2 = pool.enqueue([](int a) { return a * a; }, 3); std::cout << "result1: " << result1.get() << std::endl; // 输出3 std::cout << "result2: " << result2.get() << std::endl; // 输出9 return 0; } ``` ### 关键点说明 1. **任务队列**:使用`std::queue`存储待执行的任务,每个任务是一个`std::function<void()>`类型的可调用对象。 2. **工作线程**:在构造函数中创建指定数量的线程,每个线程循环等待任务并执行。 3. **同步机制**:使用互斥锁`std::mutex`保护任务队列,条件变量`std::condition_variable`用于线程等待和唤醒。 4. **任务添加**:`enqueue`函数模板将任意可调用对象及其参数打包成任务,并返回一个`std::future`以便获取结果。 5. **停止处理**:在析构函数中设置`stop`标志为`true`,并通知所有线程退出循环。 ### 实际应用注意事项 - **异常安全**:当前实现中,如果任务抛出异常,异常会被传递到`future`的`get()`方法中,由调用者处理。但线程池本身不会因为一个任务的异常而停止。 - **资源管理**:确保在析构时正确停止所有线程,避免线程泄漏。 - **性能考虑**:任务队列使用互斥锁可能成为瓶颈,可以考虑无锁队列或任务窃取(work-stealing)等高级技术。 - **动态调整线程数**:更高级的线程池可以根据任务负载动态增加或减少线程数量。 ### 相关问题 1. 如何避免线程池中的任务饥饿? 2. 线程池中如何实现任务优先级? 3. 如何设计一个支持动态调整线程数量的线程池? 4. 线程池在分布式系统中的应用有哪些? 5. 如何处理线程池中的任务依赖关系? [^1]: 参考自C++11线程池的常见实现模式,以及《C++ Concurrency in Action》中的相关设计。</think>### C++11线程池实现与实战指南 #### 一、线程池核心设计 线程池的核心组件包括: 1. **任务队列**:存储待执行的函数对象 2. **工作线程组**:执行任务的线程集合 3. **同步机制**:使用互斥锁和条件变量 4. **停止标志**:安全终止线程池 ```cpp #include <vector> #include <thread> #include <mutex> #include <condition_variable> #include <queue> #include <functional> #include <future> class ThreadPool { public: explicit ThreadPool(size_t threads) : stop(false) { for(size_t i = 0; i < threads; ++i) workers.emplace_back([this] { while(true) { std::function<void()> task; { std::unique_lock<std::mutex> lock(this->queue_mutex); this->condition.wait(lock, [this]{ return this->stop || !this->tasks.empty(); }); if(this->stop && this->tasks.empty()) return; task = std::move(this->tasks.front()); this->tasks.pop(); } task(); } }); } template<class F, class... Args> auto enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> { using return_type = typename std::result_of<F(Args...)>::type; auto task = std::make_shared< std::packaged_task<return_type()> >( std::bind(std::forward<F>(f), std::forward<Args>(args)...) ); std::future<return_type> res = task->get_future(); { std::unique_lock<std::mutex> lock(queue_mutex); if(stop) throw std::runtime_error("enqueue on stopped ThreadPool"); tasks.emplace([task](){ (*task)(); }); } condition.notify_one(); return res; } ~ThreadPool() { { std::unique_lock<std::mutex> lock(queue_mutex); stop = true; } condition.notify_all(); for(std::thread &worker: workers) worker.join(); } private: std::vector<std::thread> workers; std::queue<std::function<void()>> tasks; std::mutex queue_mutex; std::condition_variable condition; bool stop; }; ``` #### 二、关键实现解析 1. **任务封装**:使用`std::packaged_task`和`std::future`实现异步结果获取 2. **完美转发**:`std::forward`保持参数类型特性 3. **条件变量**:`condition.wait`避免忙等待 4. **RAII管理**:析构函数自动停止所有线程 #### 三、实战应用示例 ```cpp // 计算斐波那契数列(模拟CPU密集型任务) int fibonacci(int n) { if(n <= 1) return n; return fibonacci(n-1) + fibonacci(n-2); } int main() { ThreadPool pool(4); // 创建4个线程 // 提交多个任务 auto result1 = pool.enqueue([] { return fibonacci(35); }); auto result2 = pool.enqueue([] { return fibonacci(40); }); // 获取结果(会阻塞直到任务完成) std::cout << "Result 1: " << result1.get() << "\n"; std::cout << "Result 2: " << result2.get() << "\n"; // 批量提交任务 std::vector<std::future<int>> results; for(int i = 0; i < 8; ++i) { results.emplace_back( pool.enqueue([i] { std::this_thread::sleep_for(std::chrono::seconds(1)); return i*i; }) ); } for(auto&& result : results) std::cout << result.get() << ' '; } ``` #### 四、性能优化技巧 1. **任务窃取**:实现work-stealing队列减少锁竞争 2. **动态扩缩容**:根据队列长度动态增减线程 3. **优先级队列**:使用`std::priority_queue`实现任务优先级 4. **批量提交**:减少锁的获取/释放次数 ```cpp // 批量提交优化示例 template<typename InputIt> void enqueue_bulk(InputIt first, InputIt last) { std::unique_lock<std::mutex> lock(queue_mutex); while(first != last) { tasks.emplace(*first); ++first; } condition.notify_all(); } ``` #### 五、实际应用场景 1. **网络服务器**:处理并发连接请求 2. **数据处理流水线**:图像处理/数据分析 3. **游戏引擎**:并行处理物理计算 4. **科学计算**:矩阵运算/数值模拟 > 注意事项:避免在任务中抛出未捕获异常,建议使用`try/catch`包装任务函数[^1]。
评论 31
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值