线程池源码
#pragma once
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <queue>
#include <functional>
#include <memory>
#include <future>
class ThreadPool
{
public:
ThreadPool(uint32_t size) : stop(false)
{
for (int i = 0; i < size; ++i)
{
works.emplace_back([this]() {
while (true) {
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(queueMtx);
cv.wait(lock, [this]()->bool {
return stop || !tasks.empty();
});
if (stop && tasks.empty())
return;
task = tasks.front();
tasks.pop();
}
task();
}
});
}
}
~ThreadPool()
{
{
std::unique_lock<std::mutex> lock(queueMtx);
stop = true;
}
cv.notify_all();
for (auto &work : works)
work.join();
}
template<class Func, class ...Arg>
auto enqueue(Func &&f, Arg ...args) -> std::future<typename std::invoke_result<Func, Arg...>::type>
{
using return_type = typename std::invoke_result<Func, Arg...>::type;
auto task = std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<Func>(f), std::forward<Arg>(args)...));
std::future<return_type> res = task->get_future();
{
std::unique_lock<std::mutex> lock(queueMtx);
if (stop)
throw("thread pool is stop");
tasks.emplace([task]() {(*task)(); });
cv.notify_one();
}
return res;
}
private:
bool stop;
std::vector<std::thread> works;
std::queue<std::function<void()>> tasks;
std::mutex queueMtx;
std::condition_variable cv;
};
线程池源码分析
1,condition_variable:
条件变量(condition variable):在C++11中实现多个线程之间的同步操作,当条件不满足时,相关线程一直被阻塞,直到某种条件成立,这些线程才会被唤醒。
条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包含两个动作:
一个线程因为等待条件变量的条件成立而挂起,
另外一个线程使条件成立,给出信号,从而唤醒被等待的线程。
为了防止竞争,条件变量总是和一个互斥锁结合在一起,通常情况下这个锁是 std::mutex,并且管理这个锁的只能是 std::unique_lock<std::mutex> RAII 的模板类。
这里我们使用condition_variable实现线程同步,当没有任务执行时,condition_variable使线程处于等待状态,当有任务进入队列后,调用notify_one唤醒一个等待的线程处理任务,处理完成后线程又恢复到等待状态等待下一次被唤醒。
2, std::unique_lock<std::mutex>:
std::future是C++11标准库(并发支持库)中的一个模板类,它表示一个异步操作的结果。当我们在多线程编程中使用异步任务时,std::future可以帮助我们在需要的时候获取任务的执行结果。std::future的一个重要特性是能够阻塞当前线程,直到异步操作完成,从而确保我们在获取结果时不会遇到未完成的操作。
作用
- 异步操作的结果获取:std::future提供了一种机制,允许我们在多线程环境中安全地获取异步操作的结果。
- 隐藏异步操作的细节:std::future将异步操作的结果封装起来,使程序员无需关注线程同步和通信的具体实现细节。
- 线程同步:通过阻塞等待异步操作完成,std::future可以确保我们在继续执行其他操作之前,已经获取了所需的结果。
- 异常处理:std::future可以捕获异步操作中抛出的异常,并在获取结果时重新抛出,也可以在主线程中对其进行处理,从而使得异常处理更加简单。
- 提高性能:std::future使得我们能够更好地利用多核处理器的性能,通过并行执行任务来提高程序的执行效率。
5,std::packaged_task
td::packaged_task 将任何可调用对象(比如函数、lambda 表达式等等)封装成一个 task,可以异步执行。执行结果可以使用 std::future 获取。
6,std::bind
std::bind将可调用对象与其参数一起进行绑定,绑定后的结果可以使用std::function保存。std::bind主要有以下两个作用:
- 将可调用对象和其参数绑定成一个仿函数;
- 只绑定部分参数,减少可调用对象传入的参数。
7,std::forward
实现参数完美转发
8,queue
任务队列,先进先出,保存待执行的任务。
9,vector
向量,用于保存线程对象,后面管理线程。
测试程序
#include "threadpool.h"
int main(void)
{
int size = 4;
int a = 0;
int b = 0;
ThreadPool threadpool(size);
for (int i = 0; i < size; ++i)
{
a = i * 3;
b = (i + 1) * 2;
auto res = threadpool.enqueue([a, b]()->int {return a + b; });
std::cout << "a = " << a << " b = " << b << " res = " << res.get()<< std::endl;
}
return 0;
}