一、什么是线程池
在介绍如何实现一个高效的线程池之前,我们首先需要了解线程池的概念。
线程池是一种多线程处理方式,旨在避免在应用程序中重复创建和销毁线程带来的开销,通过事先创建一定数量的线程存放在一个线程池中,当有任务需要处理时,就从线程池中取一个线程来执行。当任务完成后,该线程不销毁,而是返回线程池中,等待接下来的任务处理。
线程池可以提高多线程编程的效率,减少线程的创建次数,避免线程数目过多的情况下出现的各种问题。接下来,我们将通过代码实现来进一步了解线程池的运作原理。
二、如何实现一个线程池
下面是一个简单的线程池实现的代码示例:
#include #include #include #include #include class ThreadPool { public: ThreadPool(size_t num_threads) { for (size_t i = 0; i < num_threads; ++i) { threads_.emplace_back([this] { for (;;) { std::function task; { std::unique_lock lock(mutex_); condition_.wait(lock, [this] { return stop_ || !tasks_.empty(); }); if (stop_ && tasks_.empty()) return; task = std::move(tasks_.front()); tasks_.pop(); } task(); } }); } } template auto enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of::type> { using return_type = typename std::result_of::type; auto task = std::make_shared<std::packaged_task> (std::bind(std::forward(f), std::forward(args)...)); std::future res = task->get_future(); { std::unique_lock lock(mutex_); if (stop_) throw std::runtime_error("enqueue on stopped ThreadPool"); tasks_.emplace([task]() { (*task)(); }); } condition_.notify_one(); return res; } ~ThreadPool() { { std::unique_lock lock(mutex_); stop_ = true; } condition_.notify_all(); for (std::thread &worker : threads_) worker.join(); } private: std::vector threads_; std::queue<std::function> tasks_; std::mutex mutex_; std::condition_variable condition_; bool stop_ = false; };
这个线程池实现包含了一个ThreadPool类和三个对外的接口函数:
- ThreadPool(size_t num_threads),一个构造函数,用于创建指定数量的线程。
- enqueue(F&& f, Args&&… args),用于向线程池中添加需要处理的任务。
- ~ThreadPool(),线程池的析构函数,用于销毁全部的线程。
三、线程池的使用
使用这个线程池很简单。我们只需要先创建一个ThreadPool对象并指定线程数量,然后通过enqueue函数向线程池添加需要处理的任务。下面是一个示例代码:
ThreadPool pool(4); auto result = pool.enqueue([] (int x) { return x * x; }, 42); std::cout << result.get() << std::endl;
这段代码首先创建一个ThreadPool对象pool,并指定线程数量为4。接着通过enqueue函数向线程池添加任务:一个lambda函数,用于计算给定的整数的平方。最后将任务的返回结果打印出来。
四、线程池的优化
上述的线程池实现非常基础,但它仍然有些可以优化的地方。例如,它只有一个任务队列和一个条件变量,这里有些风险,如果enqueue函数中有异常抛出,轻则导致队列中存在错误的任务,重则导致整个线程池崩溃。
因此,我们可以为任务队列和条件变量各自设置一个锁,使得enqueue函数成为线程安全的函数,保证任务进入队列的正确性。请看下面的代码示例:
class ThreadPool { public: ThreadPool(size_t num_threads) : stop_(false) { for (size_t i = 0; i < num_threads; ++i) { threads_.emplace_back([this] { for (;;) { std::function task(nullptr); { std::unique_lock lock(mutex_); condition_.wait(lock, [this] { return stop_ || !tasks_.empty(); }); if (stop_ && tasks_.empty()) return; task = std::move(tasks_.front()); tasks_.pop(); } task(); } }); } } template auto enqueue(F&& f, Args&&... args) ->std::future<typename std::result_of::type> { using return_type = typename std::result_of::type; auto task = std::make_shared< std::packaged_task>( std::bind(std::forward(f), std::forward(args)...)); std::future res = task->get_future(); { std::unique_lock lock(mutex_); if (stop_) throw std::runtime_error("enqueue on stopped ThreadPool"); tasks_.emplace([task]() { (*task)(); }); } condition_.notify_one(); return res; } ~ThreadPool() { { std::unique_lock lock(mutex_); stop_ = true; } condition_.notify_all(); for (std::thread& worker : threads_) worker.join(); } private: std::vector threads_; std::queue<std::function> tasks_; std::mutex task_mutex_; std::mutex stop_mutex_; std::mutex mutex_; std::condition_variable condition_; bool stop_; };
五、总结
本文从线程池的概念和原理入手,介绍了如何使用C++语言实现一个基本的线程池。同时,本文还提到了线程池的优化点,为实际生产环境中的应用提供了一些需要注意的技巧。
线程池是多线程编程中一种比较常见的工具,它可以提高程序的并发处理能力,避免线程频繁的创建和销毁对系统带来的负担。希望本文能够帮助大家理解线程池的意义以及实现方法,对提高应用程序性能有所帮助。
原创文章,作者:BXWM,如若转载,请注明出处:https://www.506064.com/n/131897.html