std::jthread は、C++20 で導入された 「自動キャンセル機能付きの安全なスレッド」 です。従来の std::thread の欠点を補い、より扱いやすく、例外安全なスレッド管理を可能にします。
C++標準には、非同期処理を行う方法に std::thread, std::jthread, std::async の3つがあります。
最初にこの3つについて概要を記載します。
| 目的 | 推奨される選択肢 |
|---|---|
| スレッドを完全に制御したい |
std::thread |
| 安全にスレッドを扱いたい |
std::jthread |
| 戻り値が必要な非同期処理を簡単に書きたい |
std::async |
| 特徴 | 最も基本的なスレッド。スレッドの生成と管理を自分で行う。 |
| 利点 | 細かい制御が可能(スレッドのライフサイクル、優先度など) |
| 注意点 |
|
| 使いどころ | スレッドの寿命や動作を細かく制御したいとき |
| 特徴 | std::thread
の改良版。スレッド終了時に自動で join() してくれる |
| 利点 |
|
| 注意点 |
|
| 使いどころ | スレッドのキャンセルや自動管理が欲しいとき。 C++20 を使えるなら std::thread よりこちらの方が安全。 |
| 特徴 | 関数を非同期に実行し、std::future
で結果を取得。 |
| 利点 |
|
| 注意点 |
|
| 使いどころ | 「関数を非同期に呼び出して結果を待ちたい」ようなシンプルなケースに最適 |
以下、本ページでは std::jthread について記載します。
std::jthread は以下のような特徴があります。
| 機能 | std::thread | std::jthread |
|---|---|---|
| デストラクタで join | ❌ しない | ✅ 自動で join |
| キャンセル機構 | ❌ なし | ✅ stop_token で協調的キャンセル |
| detach | あり | なし |
| 例外安全性 | 低い | 高い |
| C++ バージョン | C++11〜 | C++20〜 |
まずは「stop_token による協調的キャンセル」の機能を試してみます。
[環境]
| コンパイラ : | g++ (Ubuntu 13.3.0-6ubuntu2~24.04), | 13.3.0 |
| OS: | Ubuntu 24.04 (WSL), | |
[プログラムソース] jthread_01.cpp
#include <iostream> // cout, endl
#include <cstdlib> // EXIT_SUCCESS
#include <string> // string
#include <chrono> // chrono
#include <thread> // jthread
#include <mutex> // mutex, lock_guard
// グローバルなミューテックス
std::mutex printMutex;
// スレッドセーフなメッセージ出力関数
void printMessage(const std::string& message) {
std::lock_guard<std::mutex> lock(printMutex);
std::cout << message << std::endl;
}
// スレッド関数
void threadFunction( std::stop_token s, const std::string& title ) {
// 停止要求が来るまでループ
while (!s.stop_requested()) {
// 定期的にメッセージを出力
printMessage("Thread is running: " + title);
std::this_thread::sleep_for(std::chrono::seconds{1});
}
printMessage(title + " has received stop message.");
std::this_thread::sleep_for(std::chrono::seconds{3});
printMessage(title + " has been stopped.");
}
// メイン関数
int main(){
// jthreadを使用して停止可能なスレッドを作成
std::jthread worker{threadFunction, "[Thread A]"};
// jthreadは自動的にスレッドをjoinするので、明示的なjoinは不要です。
// メインスレッドも何か処理を行う
for (int i = 0; i < 3; ++i) {
printMessage("Main thread is running: " + std::to_string(i));
std::this_thread::sleep_for(std::chrono::seconds{3});
}
// スレッドに対して停止要求を送る
worker.request_stop();
printMessage("Stop request sent to worker thread.");
// jthread はスコープを抜けると自動的に join されます
// ここで worker.join() を呼ぶこともできますが、jthread は自動的に join されるため、通常は不要です
return EXIT_SUCCESS;
}
ビルドおよび実行結果:
$ g++ -std=c++20 -Wall -Wextra -Wpedantic -pthread -O2 jthread_sample_01.cpp -o jthread_sample_01.out $ ./jthread_sample_01.out Main thread is running: 0 Thread is running: [Thread A] Thread is running: [Thread A] Thread is running: [Thread A] Main thread is running: 1 Thread is running: [Thread A] Thread is running: [Thread A] Thread is running: [Thread A] Main thread is running: 2 Thread is running: [Thread A] Thread is running: [Thread A] Thread is running: [Thread A] Stop request sent to worker thread. [Thread A] has received stop message. [Thread A] has been stopped. $
| オプション | 説明 |
|---|---|
| -std=c++20 | C++20 標準でコンパイルします。言語機能(構文・ライブラリ仕様)が C++20 準拠になります。 |
| -Wall | 一般的な警告群を有効にします。コード品質の初期チェックに有用です。 |
| -Wextra | -Wall に含まれない追加の警告を有効にします。未使用パラメータなどを検出します。 |
| -Wpedantic | 規格に厳格に従っていない拡張や非標準的記法を警告します。移植性を高めたい場合に有効です。 |
| -pthread | POSIX threads のサポートを有効にし、スレッド関連のライブラリをリンクします。単に -lpthread をリンクするだけでなく、コンパイル時にスレッド対応(スレッドセーフな定義など)を有効にするフラグです。スレッド/ミューテックスなどを使うプログラムでは必須に近いオプションです(Linux)。 |
| -O2 | 最適化レベル 2 を指定します。実行速度向上のための最適化を行うが、"-O3" よりは穏やかで安定的な設定です。デバッグ時は "-g -O0" を推奨します。 |
スレッドへの停止要求を送ることで、スレッドを停止できることを確認できました。
起動中のスレッドに対して request_stop() しないでプログラムを終了した場合、どんな挙動になるのかを確認してみます。
結論から記載すると、jthread インスタンス解放時に自動的に request_stop() を発行してくれるようです。最初に記載の 特徴3 になります。
前述の jthread_01.cpp を元に作成するスレッドを3つに拡張したものを使用し、stop_request() をコメントアウトしたサンプルプログラムを使用します。
[環境]
| コンパイラ : | g++ (Ubuntu 13.3.0-6ubuntu2~24.04), | 13.3.0 |
| OS: | Ubuntu 24.04 (WSL), | |
[プログラムソース] jthread_02.cpp
#include <iostream> // cout, endl
#include <cstdlib> // EXIT_SUCCESS
#include <string> // string
#include <chrono> // chrono
#include <thread> // jthread
#include <mutex> // mutex, lock_guard
// グローバルなミューテックス
std::mutex printMutex;
// スレッドセーフなメッセージ出力関数
void printMessage(const std::string& message) {
std::lock_guard<std::mutex> lock(printMutex);
std::cout << message << std::endl;
}
// スレッド関数
void threadFunction( std::stop_token s, const std::string& title ) {
// 停止要求が来るまでループ
while (!s.stop_requested()) {
// 定期的にメッセージを出力
printMessage("Thread is running: " + title);
std::this_thread::sleep_for(std::chrono::seconds{1});
}
printMessage(title + " has received stop message.");
std::this_thread::sleep_for(std::chrono::seconds{3});
printMessage(title + " has been stopped.");
}
// メイン関数
int main(){
// jthreadを使用して停止可能なスレッドを作成
std::jthread worker1{threadFunction, "[Thread A]"};
std::jthread worker2{threadFunction, "[Thread B]"};
std::jthread worker3{threadFunction, "[Thread C]"};
// jthreadは自動的にスレッドをjoinするので、明示的なjoinは不要です。
// メインスレッドも何か処理を行う
for (int i = 0; i < 3; ++i) {
printMessage("Main thread is running: " + std::to_string(i));
std::this_thread::sleep_for(std::chrono::seconds{3});
}
// スレッドに対して停止要求を送る
//worker1.request_stop();
//worker2.request_stop();
//worker3.request_stop();
printMessage("Stop request sent to worker thread.");
// jthreadはスコープを抜けると自動的にjoinされます
// ここで worker.join() を呼ぶこともできますが、jthread は自動的に join されるため、通常は不要です
return EXIT_SUCCESS;
}
ビルドおよび実行:
$ g++ -std=c++20 -Wall -Wextra -Wpedantic -pthread -O2 jthread_sample_02.cpp -o jthread_sample_02.out $ ./jthread_sample_02.out Main thread is running: 0 Thread is running: [Thread C] Thread is running: [Thread B] Thread is running: [Thread A] Thread is running: [Thread A] Thread is running: [Thread C] Thread is running: [Thread B] Thread is running: [Thread A] Thread is running: [Thread C] Thread is running: [Thread B] Main thread is running: 1 Thread is running: [Thread A] Thread is running: [Thread C] Thread is running: [Thread B] Thread is running: [Thread A] Thread is running: [Thread C] Thread is running: [Thread B] Thread is running: [Thread A] Thread is running: [Thread C] Thread is running: [Thread B] Main thread is running: 2 Thread is running: [Thread A] Thread is running: [Thread C] Thread is running: [Thread B] Thread is running: [Thread A] Thread is running: [Thread B] Thread is running: [Thread C] Thread is running: [Thread A] Thread is running: [Thread B] Thread is running: [Thread C] Stop request sent to worker thread. Thread is running: [Thread B] [Thread C] has received stop message. Thread is running: [Thread A] Thread is running: [Thread B] Thread is running: [Thread A] Thread is running: [Thread B] Thread is running: [Thread A] [Thread C] has been stopped. [Thread B] has received stop message. Thread is running: [Thread A] Thread is running: [Thread A] Thread is running: [Thread A] [Thread B] has been stopped. [Thread A] has received stop message. [Thread A] has been stopped. $
全部のスレッドに対して自動的に request_stop() を発行してくれることを確認できました。
ただし、コンソール出力内容を確認すると、まとめて全部に request_stop() を送信してくれるわけではなさそうです。一つずつ request_stop() -> join() しているようなので、プログラム終了後に恐らく以下と同等の処理をやってると思います。
// スレッドに対して順番に停止要求を送り、その後 join() して終了を待つ
{
std::jthread* workers[] = { &worker1, &worker2, &worker3 };
for (std::jthread* w : workers) {
printMessage("Sending stop request to a worker...");
w->request_stop();
// 停止処理が完了するのを待つ
if (w->joinable()) {
w->join();
printMessage("Worker has been joined.");
}
}
}
本ページの情報は、特記無い限り下記 MIT ライセンスで提供されます。
| 2026-02-04 | - | 「4. request_stop() しないとどうなる?」を追加 |
| 2026-02-01 | - | 新規作成 |