Как написать типизированную обёртку над простым тред-пулом?

Написание типизированной обёртки над простым тред-пулом в C++ - это процесс создания абстракции, которая скрывает детали управления потоками, чтобы облегчить выполнение параллельных задач.

Прежде всего, требуется создать класс, представляющий типизированную обёртку над тред-пулом. Назовем его ThreadPool.

#include <functional>
#include <thread>
#include <vector>
#include <queue>
#include <mutex>
#include <condition_variable>

template<typename T>
class ThreadPool {
public:
    ThreadPool(size_t numThreads) : running(true) {
        for (size_t i = 0; i < numThreads; ++i) {
            workers.emplace_back([this]() {
                while (running) {
                    std::unique_lock<std::mutex> lock(mtx);
                    cond.wait(lock, [this]() { return !running || !tasks.empty(); });
                    
                    if (!running && tasks.empty()) {
                        return;
                    }
                    
                    auto task = std::move(tasks.front());
                    tasks.pop();
                    lock.unlock();
                    
                    task();
                }
            });
        }
    }
    
    ~ThreadPool() {
        {
            std::unique_lock<std::mutex> lock(mtx);
            running = false;
        }
        
        cond.notify_all();
        
        for (auto& worker : workers) {
            worker.join();
        }
    }
    
    template<typename F, typename... Args>
    auto enqueue(F&& f, Args&&... args) -> std::future<T> {
        using return_type = typename std::result_of<F(Args...)>::type;
        
        auto task = std::make_shared<std::packaged_task<return_type()>>(
            std::bind(std::forward<F>(f), std::forward<Args>(args)...)
        );
        
        std::future<T> res = task->get_future();
        
        {
            std::unique_lock<std::mutex> lock(mtx);
            if (!running) {
                throw std::runtime_error("ThreadPool is stopped");
            }
            
            tasks.emplace([task]() { (*task)(); });
        }
        
        cond.notify_one();
        
        return res;
    }
    
private:
    std::vector<std::thread> workers;
    std::queue<std::function<void()>> tasks;
    
    std::mutex mtx;
    std::condition_variable cond;
    
    bool running;
};

Давайте разберём этот код по частям:

1. Включаем необходимые заголовочные файлы: functional, thread, vector, queue, mutex и condition_variable.
2. Создаем класс ThreadPool с использованием шаблона для типизации.
3. Cоздаем конструктор, принимающий количество потоков, которые будут доступны в пуле. Внутри конструктора мы создаем заданное количество потоков и помещаем их в массив workers. Каждый поток выполняет бесконечный цикл, в котором он ждет, пока есть задачи и пока флаг running установлен в true. Когда поток просыпается, он получает задачу из очереди tasks, выполняет ее и повторяет процесс до тех пор, пока running равен true.
4. Создаем деструктор для ThreadPool. Внутри деструктора мы сначала устанавливаем флаг running в false, чтобы остановить потоки. Затем мы оповещаем все потоки остановиться, используя условную переменную cond, и дожидаемся, пока все потоки завершатся с помощью вызова join() для каждого потока в массиве workers.
5. Создаем метод enqueue для добавления задачи в очередь. Метод enqueue принимает произвольную функцию f и ее аргументы args. Он создает упакованную задачу, используя std::bind и std::forward для передачи аргументов. Затем мы помещаем упакованную задачу в очередь tasks, где ее выполнение будет ожидать поток из пула. Метод возвращает std::future, который можно использовать для получения результата выполнения задачи.

Обратите внимание, что мы используем std::result_of для определения возвращаемого типа задачи, и используем std::forward для передачи аргументов в упакованную задачу.

Это основа типизированной обёртки над простым тред-пулом в C++. Вы можете использовать этот класс ThreadPool для создания параллельных задач и эффективного управления потоками. Он предоставляет гибкий и типобезопасный механизм для выполнения параллельных задач в C++.