【C++】スレッドを安全に停止させる方法 (jthread と stop_token)

目次

はじめに

マルチスレッドプログラミングにおける難しい課題の一つが、「スレッドを安全に、かつ綺麗に停止させる」ことです。旧来のstd::threadでは、スレッドを外部から中断させるための標準的な仕組みがなく、複雑な実装が必要でした。

この問題を解決するために、C++20という新しい規格で中断の仕組みを内蔵した新しいスレッドクラス std::jthread が導入されました。

この記事では、jthread を使って、中断可能なワーカースレッドを作成し、メインスレッドから安全に停止させる方法を解説します。


【前提】C++20とは?

C++というプログラミング言語は、数年ごとに改訂され、新しい機能が追加されたり、より安全で便利になったりしています。その改訂版には、発行された年にちなんで名前が付けられます。

C++20(シーピープラスにじゅう、またはにーぜろ)は、2020年に正式化された、C++言語のメジャーアップデート版のことです。

この記事で紹介する std::jthreadstd::stop_token は、このC++20で新たに追加された機能です。そのため、これらのコードをコンパイルするには、C++20に対応したコンパイラ(と、その機能を有効にするための設定)が必要になります。


jthread を使った中断可能なスレッドのサンプルコード

このコードは、worker_task というバックグラウンドタスクを jthread で起動します。main関数は3秒待った後、スレッドに対して停止を要求します。ワーカースレッドは、ループの各回で停止要求が来ていないかを確認し、要求を検知したらループを抜けて正常に終了します。

完成コード

#include <iostream>
#include <thread> // jthread, stop_token を使うために必要
#include <chrono>

using namespace std;

// 1. スレッドで実行されるタスク
//    最初の引数で std::stop_token を受け取る
void worker_task(stop_token st, const string& task_name) {
    int counter = 0;
    
    // 2. stop_requested()で、外部から停止要求が来ていないかチェック
    while (!st.stop_requested()) {
        cout << task_name << ": 実行中... (" << ++counter << "秒経過)" << endl;
        // 1秒待機
        this_thread::sleep_for(chrono::seconds(1));
    }
    
    // ループを抜けたら、停止要求を検知したことを示す
    cout << task_name << ": 停止要求を検知。タスクを終了します。" << endl;
}

int main() {
    cout << "メインスレッド: ワーカースレッドをjthreadで起動します。" << endl;
    
    // 3. jthreadでスレッドを起動
    jthread worker(worker_task, "タスクA");
    
    cout << "メインスレッド: 3秒後に停止要求を送ります。" << endl;
    this_thread::sleep_for(chrono::seconds(3));

    // 4. スレッドに停止を要求する
    worker.request_stop();
    
    // 5. jthreadはデストラクタで自動的にjoin()されるので、明示的なjoinは不要
    
    cout << "メインスレッド: 終了。" << endl;
    return 0;
}

コードの解説

1. void worker_task(stop_token st, ...)

jthread で起動する関数の最初の引数std::stop_token 型にすると、そのスレッドに関連付けられた「停止トークン」を自動的に受け取ることができます。この stop_token オブジェクトを通じて、外部からの停止要求を検知します。

2. while (!st.stop_requested())

これが、スレッドが自律的に停止するための核心部分です。

  • st.stop_requested(): 停止要求があれば true を、なければ false を返します。
  • この while ループは、停止要求が来るまで処理を続けます。

ワーカースレッドは、このように定期的に stop_requested() をチェックし、true になったら後片付けの処理などを行って、自ら処理を完了させるのが、安全な中断処理の基本です。

3. jthread worker(...)

std::jthreadstd::thread とほぼ同じように使えます。コンストラクタに、実行したい関数と、その関数に渡す引数を指定します。

4. worker.request_stop()

mainスレッドから、worker スレッドに対して「停止してください」という要求を送るためのメソッドです。これを呼び出すと、worker スレッドに紐付いた stop_token の状態が「停止要求あり」に変わります。

5. jthread の自動 join

通常の std::thread は、破棄される前に必ず .join().detach() を呼ぶ必要がありましたが、jthreadデストラクタが呼ばれる際に、自動的に .join() を呼び出してくれます。これにより、join の呼び忘れによるプログラムのクラッシュを防ぐことができ、コードがより安全かつシンプルになります。


まとめ

今回は、C++20の jthreadstop_token を使って、安全に中断可能なスレッドを作成する方法を解説しました。

  • std::jthread: 中断機構と自動 join 機能を備えた、安全なスレッドクラス。
  • 関数の最初の引数を std::stop_token にすると、停止トークンを受け取れる。
  • ループの中で stop_token.stop_requested() を定期的にチェックし、スレッドが自律的に終了するように実装する。
  • 外部からは jthread.request_stop() で、スレッドに停止要求を送る。

jthread は、従来の thread が抱えていた問題を解決する、現代C++におけるスレッドプログラミングの新しい標準です。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次