C++20で導入されたstd::jthread
は、スレッドを中断させる仕組みをもった便利なクラスです。今回は、C++14でそれを簡易的に実装してみました (C++14の機能としてはラムダ式の初期化キャプチャを使っています。ラムダ式に外の変数をムーブするために必要)。
std::thread
+ std::promise
とstd::future
を使えばできます。
std::promise
に値を設定することで中断リクエストし、スレッド側のstd::future
はstd::promise
に値が設定されるまで (中断リクエストされるまで) 処理を続けます。
std::promise
には1回しか値を設定できないので (2回設定すると例外が投げられる)、2回以上設定されないようにする必要があります。
#include <thread> #include <future> #include <utility> class join_thread { bool _stop_requested = false; std::promise<void> _stop_request; std::thread _thread; public: template <class F> explicit join_thread(F f) { _thread = std::thread { [g = std::move(f), fut = _stop_request.get_future()] mutable { g(std::move(fut)); } }; } ~join_thread() { join(); } void request_stop() { if (!_stop_requested) { _stop_requested = true; _stop_request.set_value(); } } void join() { request_stop(); if (_thread.joinable()) { _thread.join(); } } }; #include <iostream> #include <chrono> void f(std::future<void> stop_request) { int sum = 0; // 0秒waitでfutureに値が設定されたかを確認 while (stop_request.wait_for(std::chrono::seconds(0)) != std::future_status::ready) { ++sum; } std::cout << sum << std::endl; } int main() { join_thread t{f}; std::this_thread::sleep_for(std::chrono::milliseconds(3)); t.request_stop(); t.join(); }
出力例:
32244
あとがき:
std::promise
とstd::future
の代わりに、std::atomic<bool>
をスレッド間で共有するとかでもよかったですね…そちらの方がコストが安そう。実際はどちらでもよいですが、std::thread
を使ったスレッドに、いろいろな方法で外から中断リクエストを送れる、ということでした。
std::atomic<bool>
を使ったバージョンも載せておきます。
#include <thread> #include <atomic> #include <utility> class join_thread { std::atomic<bool> _stop_request{false}; std::thread _thread; public: template <class F> explicit join_thread(F f) { _thread = std::thread{ [this, g = std::move(f)] mutable { g(_stop_request); } }; } ~join_thread() { join(); } void request_stop() { _stop_request.store(true); } void join() { request_stop(); if (_thread.joinable()) { _thread.join(); } } }; #include <iostream> #include <chrono> void f(std::atomic<bool>& stop_request) { int sum = 0; while (!stop_request.load()) { ++sum; } std::cout << sum << std::endl; } int main() { join_thread t{f}; std::this_thread::sleep_for(std::chrono::milliseconds(3)); t.request_stop(); t.join(); }