Loading

【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

参考

https://blog.csdn.net/qq_53111905/article/details/123301962

posted @ 2024-03-08 14:58  rthete  阅读(317)  评论(0编辑  收藏  举报