[c++] C++ 스레드 풀과 작업 취소 처리

본 포스트에서는 C++에서 스레드 풀을 구현하고, 작업 취소 처리를 하는 방법에 대해 알아봅니다.

스레드 풀의 개념

스레드 풀은 여러 작업을 처리하기 위해 미리 스레드를 생성해 놓고, 작업이 들어오면 이 스레드들을 이용해 병렬로 처리하는 기법입니다. 보통 여러 스레드를 동시에 생성하는 것보다 스레드 풀을 사용하는 것이 성능상 이점이 있습니다.

C++에서의 스레드 풀 구현

C++에서는 std::thread를 사용하여 스레드를 생성하고 관리할 수 있습니다. 스레드 풀을 구현하기 위해서는 스레드를 미리 생성해 놓고, 작업 큐를 만들어서 스레드가 작업을 가져오도록 합니다. 또한, 스레드가 종료되지 않고 다음 작업을 처리할 수 있도록 루프를 추가하여야 합니다.

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

class ThreadPool {
public:
    ThreadPool(size_t numThreads) : stop(false) {
        for (size_t i = 0; i < numThreads; ++i) {
            workers.emplace_back([this] {
                while (true) {
                    std::function<void()> task;
                    {
                        std::unique_lock<std::mutex> lock(this->queueMutex);
                        this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); });
                        if (this->stop && this->tasks.empty()) {
                            return;
                        }
                        task = std::move(this->tasks.front());
                        this->tasks.pop();
                    }
                    task();
                }
            });
        }
    }

    template <class F>
    void enqueue(F f) {
        {
            std::unique_lock<std::mutex> lock(queueMutex);
            if (stop) {
                throw std::runtime_error("enqueue on stopped ThreadPool");
            }
            tasks.emplace(f);
        }
        condition.notify_one();
    }

    ~ThreadPool() {
        {
            std::unique_lock<std::mutex> lock(queueMutex);
            stop = true;
        }
        condition.notify_all();
        for (std::thread &worker : workers) {
            worker.join();
        }
    }

private:
    std::vector<std::thread> workers;
    std::queue<std::function<void()>> tasks;
    std::mutex queueMutex;
    std::condition_variable condition;
    bool stop;
};

작업 취소 처리

작업 취소는 스레드가 일정 시간동안 작업이 끝나지 않으면 해당 작업을 취소하고 다른 작업을 처리하는 기능을 말합니다. 스레드 풀에서는 각 작업이 완료되었는지 확인하는 매커니즘이 필요합니다.

#include <chrono>
#include <future>

template<typename ReturnType>
class CancellableTask 
{
public:
    CancellableTask(std::function<ReturnType()> f) : task(f) { }

    ReturnType operator()() { 
        try {
            return task();
        }
        catch (const std::future_error&) {
            std::cout << "Task has been cancelled" << std::endl;
            throw;
        }
    }

    ReturnType get(std::chrono::milliseconds timeout) { 
        auto handle = std::async(std::launch::async, *this);
        auto status = handle.wait_for(timeout);
        if (status == std::future_status::timeout) {
            handle.cancel();
            throw std::future_error(std::future_errc::broken_promise);
        }
        return handle.get();
    }

private:
    std::function<ReturnType()> task;
};

위 예시는 C++에서 스레드 풀 및 작업 취소 처리의 기초적인 예시입니다. 추가적인 코드 및 라이브러리를 사용하여 보다 복잡하고 안전한 스레드 풀 및 작업 취소 처리를 구현할 수 있습니다.

결론

C++에서 스레드 풀을 구현하고, 작업 취소 처리를 하는 방법에 대해 알아보았습니다. 스레드 풀을 통해 병렬 작업을 처리할 때 작업 취소 처리를 유의하며 안전하고 성능적으로 효율적인 프로그램을 작성할 수 있습니다.

참고 문헌