一、什么是线程池
在介绍如何实现一个高效的线程池之前,我们首先需要了解线程池的概念。
线程池是一种多线程处理方式,旨在避免在应用程序中重复创建和销毁线程带来的开销,通过事先创建一定数量的线程存放在一个线程池中,当有任务需要处理时,就从线程池中取一个线程来执行。当任务完成后,该线程不销毁,而是返回线程池中,等待接下来的任务处理。
线程池可以提高多线程编程的效率,减少线程的创建次数,避免线程数目过多的情况下出现的各种问题。接下来,我们将通过代码实现来进一步了解线程池的运作原理。
二、如何实现一个线程池
下面是一个简单的线程池实现的代码示例:
#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
微信扫一扫
支付宝扫一扫