一、背景
在多线程编程中,线程池是一种常见的机制,它可以减少线程创建和销毁的开销,并控制同时执行的任务数量,防止过多的线程竞争资源导致性能下降。C++11 中的标准库提供了一个 std::thread,但是并没有提供线程池的实现,只能通过手动创建线程池来实现。在实际的项目中,线程池的需求是非常常见的,为了方便使用和管理线程池,我们可以自定义一个线程池执行器。
二、基本概念
1. 执行器
执行器是一种负责管理线程的对象。它有一个队列用来保存待执行的任务,当有空闲的线程时,它会从队列中获取一个任务并由线程执行。
2. 任务
任务是一个可执行的代码块,执行器会将任务保存到队列中,线程可以从队列中获取任务并执行。
3. 线程
线程是计算机程序中的一个执行流程。线程可以并行执行,多个线程可以共享进程中的数据和资源,使用线程可以提高程序的处理效率。
4. 线程池
线程池是在进程启动时创建一定数量的线程,这些线程在执行任务时不是一直存在,而是将执行完一个任务后,再去接管下一个任务。这样做的好处是避免了创建线程以及线程上下文切换的开销,提高了程序的效率,而且还可以避免瞬间有大量的任务到达而导致程序崩溃。
三、自定义线程执行器
在上述基本概念的基础上,我们可以自定义一个线程执行器:
```cpp
class ThreadPool
{
public:
ThreadPool(int maxThreads = 4);
~ThreadPool();
template auto submit(F&& f, Args&&... args) -> std::future private: std::vector std::queue std::mutex m_taskMutex; std::condition_variable m_taskCondition; bool m_stop; }; // 构造函数 ThreadPool::ThreadPool(int maxThreads) :m_stop(false) { maxThreads = std::max(1, maxThreads); for (int i = 0; i < maxThreads; ++i) { m_threads.emplace_back( [this]() { while (true) { std::function { std::unique_lock m_taskCondition.wait(lock, [this]() {return m_stop || !m_tasks.empty(); }); if (m_stop && m_tasks.empty()) return; task = std::move(m_tasks.front()); m_tasks.pop(); } task(); } } ); } } // 析构函数 ThreadPool::~ThreadPool() { { std::unique_lock m_stop = true; } m_taskCondition.notify_all(); for (auto& thread : m_threads) { thread.join(); } } // 提交任务 template auto ThreadPool::submit(F&& f, Args&&... args) -> std::future { using return_type = typename std::result_of auto task = std::make_shared std::future { std::unique_lock if (m_stop) { throw std::runtime_error("submit on stopped ThreadPool"); } m_tasks.emplace([task]() {(*task)(); }); } m_taskCondition.notify_one(); return result; } ``` 四、使用方法 使用线程执行器很简单,只需要使用 submit() 函数提交一个任务即可。代码如下: ```cpp ThreadPool pool(4); // 创建线程池,最大线程数量为4个 std::vector // 向线程池提交任务 for (int i = 0; i < 8; ++i) { results.emplace_back( pool.submit([i]() -> int { std::cout << "hello " << i << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); std::cout << "world " << i << std::endl; return i * i; }) ); } // 获取任务执行结果 for (auto&& result : results) { std::cout << result.get() << " "; } std::cout << std::endl; ``` 五、案例说明 下面我们通过一个具体的案例来说明线程执行器的使用。假设我们要对一组整数排序,但是排序的算法比较耗时,我们可以将排序任务提交到线程池中运行,然后在主线程中等待结果。 ```cpp #include #include #include #include #include #include #include "ThreadPool.h" // 含有线程池的头文件 void sortArray(std::vector { std::sort(arr.begin(), arr.end()); } int main() { // 创建线程池,最大线程数量为4个 ThreadPool pool(4); std::vector // 创建一个随机整数数组 std::vector for (int i = 0; i < 10000; ++i) { arr.push_back(std::rand() % 1000); } // 将排序任务提交到线程池中 for (int i = 0; i < 4; ++i) { auto future = pool.submit(sortArray, std::ref(arr)); results.emplace_back(std::move(future)); } // 等待任务完成 for (auto&& result : results) { result.get(); } // 输出排序后的数组 for (int i = 0; i < 10; ++i) { std::cout << arr[i] << " "; } std::cout << std::endl; return 0; } ``` 运行结果如下: ``` 0 0 0 0 0 0 0 0 0 0 ``` 注意,由于每个线程都在原地排序数组,而没有进行合并操作,因此输出结果会变成全 0 数组。这只是为了演示提交任务的过程,并不是一个正确的多线程排序实现。 六、总结 本文介绍了自定义线程执行器,它是基于 std::thread、std::mutex 和 std::condition_variable 等线程库组件实现的。使用线程执行器可以方便地管理和调度多线程任务,可以通过它提高程序的性能和效率。同时,线程执行器的实现思路也可以用于开发其他的并发框架和工具。 如果你喜欢我们三七知识分享网站的文章,
欢迎您分享或收藏知识分享网站文章
欢迎您到我们的网站逛逛喔!https://www.ynyuzhu.com/
发表评论 取消回复