// Copyright (C) 2026 Kiyotsugu Arai // SPDX-License-Identifier: LGPL-3.0-or-later // ThreadPool.hpp // 軽量スレッドプール — 初期化時にスレッドを起動し、 // 各演算で再利用することでスレッド生成/破棄コストを回避 #pragma once #include #include #include #include #include #include #include #include #include namespace calx { class ThreadPool { public: explicit ThreadPool(unsigned initial_threads = 0) : stop_(false), busy_(0) { if (initial_threads == 0) { unsigned hw = std::thread::hardware_concurrency(); initial_threads = (hw > 1) ? hw - 1 : 1; } for (unsigned i = 0; i < initial_threads; ++i) addWorker(); } ~ThreadPool() { { std::lock_guard lock(mutex_); stop_ = true; } cv_.notify_all(); for (auto& t : workers_) t.join(); } ThreadPool(const ThreadPool&) = delete; ThreadPool& operator=(const ThreadPool&) = delete; // タスクを投入し std::future を返す template auto submit(F&& f) -> std::future>> { using R = std::invoke_result_t>; auto task = std::make_shared>(std::forward(f)); std::future result = task->get_future(); { std::lock_guard lock(mutex_); tasks_.emplace([task]() { (*task)(); }); // 空きスレッドがなければプールを倍に拡張 unsigned total = static_cast(workers_.size()); if (busy_.load(std::memory_order_relaxed) >= total) { for (unsigned i = 0; i < total; ++i) addWorker(); } } cv_.notify_one(); return result; } private: void addWorker() { workers_.emplace_back([this]() { for (;;) { std::function task; { std::unique_lock lock(mutex_); cv_.wait(lock, [this]{ return stop_ || !tasks_.empty(); }); if (stop_ && tasks_.empty()) return; task = std::move(tasks_.front()); tasks_.pop(); busy_.fetch_add(1, std::memory_order_relaxed); } task(); busy_.fetch_sub(1, std::memory_order_relaxed); } }); } std::vector workers_; std::queue> tasks_; std::mutex mutex_; std::condition_variable cv_; bool stop_; std::atomic busy_; }; // グローバルスレッドプール (プロセスあたり 1 つ) inline ThreadPool& threadPool() { static ThreadPool pool; return pool; } } // namespace calx