【C++八股】手撕线程池&生产者-消费者模型
C++11的thread pool实现
相关特性:
thread,mutex,condition_variable,functional,atomic,template,lambda
#include <vector>
#include <queue>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <atomic>
#include <iostream>
#include <unistd.h>
class ThreadPool
{
public:
ThreadPool(size_t);
template <class F, class... Args>
void enqueue(F &&f, Args &&...args);
~ThreadPool();
private:
// need to keep track of threads so we can join them
std::vector<std::thread> workers;
// the task queue
std::queue<std::function<void()>> tasks;
// synchronization
std::mutex queue_mutex;
std::condition_variable condition;
std::atomic<bool> stop;
};
// the constructor just launches some amount of workers
inline ThreadPool::ThreadPool(size_t threads) // 消费者线程
: stop(false)
{
for (size_t i = 0; i < threads; ++i)
workers.emplace_back(
[this]
{
// 无限循环,从任务队列中获取任务
for (;;)
{
std::function<void()> task;
{
// 需要对任务队列操作,先获取锁
std::unique_lock<std::mutex> lock(this->queue_mutex);
// 等待条件成立,等待期间释放互斥锁,允许其他线程访问任务队列
// [ThreadPool停止] 或 [tasks不为空] --> 唤醒线程,重新获取锁
this->condition.wait(lock,
[this]
{ return this->stop || !this->tasks.empty(); });
// [ThreadPool停止] 且 [tasks为空] --> 工作线程返回
// 保证即使线程池停止,也要将任务队列消费完
if (this->stop && this->tasks.empty())
return;
// 将任务队列头部的任务取出执行
task = std::move(this->tasks.front());
this->tasks.pop();
}
task();
}
});
}
// add new work item to the pool
template <class F, class... Args>
void ThreadPool::enqueue(F &&f, Args &&...args) // 生产者线程
{
// 将可调用对象f与参数args绑定为一个函数对象task
auto task = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
{
// 获取互斥锁以确保对任务队列的安全访问
std::unique_lock<std::mutex> lock(queue_mutex);
// don't allow enqueueing after stopping the pool
if (stop)
throw std::runtime_error("enqueue on stopped ThreadPool");
// 将任务添加到任务队列中
tasks.emplace([task]()
{ (task)(); });
}
// 通知一个线程有新任务可用
condition.notify_one();
}
// the destructor joins all threads
inline ThreadPool::~ThreadPool()
{
// 设置stop标志为true,以通知所有线程停止工作
stop = true;
// 唤醒所有线程
condition.notify_all();
// 等待所有线程结束,并回收资源
for (std::thread &worker : workers)
worker.join();
}
void taskFunction(int id)
{
sleep(id);
std::cout << "Task " << id << " is being processed by thread " << std::this_thread::get_id() << std::endl;
}
int main()
{
// 创建一个具有4个线程的线程池
ThreadPool pool(4);
// 向线程池中添加10个任务
for (int i = 0; i < 10; ++i)
{
pool.enqueue(taskFunction, i);
}
return 0;
}
➜ threadpooldemo ./threadpoolnew
Task 0 is being processed by thread 140552499570432
Task 1 is being processed by thread 140552524748544
Task 2 is being processed by thread 140552516355840
Task 3 is being processed by thread 140552507963136
Task 4 is being processed by thread 140552499570432
Task 5 is being processed by thread 140552524748544
Task 6 is being processed by thread 140552516355840
Task 7 is being processed by thread 140552507963136
Task 8 is being processed by thread 140552499570432
Task 9 is being processed by thread 140552524748544
生产者消费者模型
#include <iostream>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <thread>
using namespace std;
template <class T>
class Queue
{
public:
void put(T val)
{
unique_lock<mutex> lock(queue_mutex);
while (!task_queue.empty())
{
condition.wait(lock);
}
task_queue.push(val);
condition.notify_all();
cout << "produce: " << val << endl;
}
T get()
{
unique_lock<mutex> lock(queue_mutex);
while (task_queue.empty())
{
condition.wait(lock);
}
T ret = task_queue.front();
task_queue.pop();
condition.notify_all();
cout << "consume: " << ret << endl;
return ret;
}
private:
mutex queue_mutex;
condition_variable condition;
queue<T> task_queue; // 任务队列中保持只有一个任务
};
int main()
{
Queue<int> my_queue;
std::thread t1([&my_queue]() -> void
{
for(int i = 0; i <= 10; i++)
{
my_queue.put(i);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
} });
std::thread t2([&my_queue]() -> void
{
for(int i = 0; i <= 10; i++)
{
my_queue.get();
std::this_thread::sleep_for(std::chrono::microseconds(100));
} });
t1.join();
t2.join();
return 0;
}
➜ threadpooldemo ./producer_consumer_demo
produce: 0
consume: 0
produce: 1
consume: 1
produce: 2
consume: 2
produce: 3
consume: 3
produce: 4
consume: 4
produce: 5
consume: 5
produce: 6
consume: 6
produce: 7
consume: 7
produce: 8
consume: 8
produce: 9
consume: 9
produce: 10
consume: 10