知識マップ: マルチスレッドの実務ベストプラクティス C++編 ── RAIIとjthreadで事故を構造から消す
記事「マルチスレッドの実務ベストプラクティス C++編 ── RAIIとjthreadで事故を構造から消す」の主張を、概念と関係(エッジ)に分解した知識グラフの全体です。各関係には根拠・確認日・確度が付いています。
C++では、同期なしの共有可変アクセスは「壊れた値が読める」では済まず、規格上の未定義動作になります。コンパイラはデータ競合が存在しない前提で最適化するため、volatileの停止フラグがリリースビルドでだけ効かないといった事故が起きます。道具立てはRAIIが土台で、joinableなままstd::threadのデストラクタが走るとstd::terminateでプロセスが即死する一方、C++20のstd::jthreadはデストラクタで停止要求とjoinを自動で行います。ロックはlock_guardやscoped_lockに任せ、複数のミューテックスはscoped_lockにまとめて渡してデッドロック回避をライブラリに保証させます。単一変数の更新はstd::atomic、複数変数の整合はstd::mutexで、volatileは同期の道具ではありません。条件変数にはスプリアスウェイクアップがあるため待機は述語付きで書き、停止はstop_tokenによる協調停止で設計します。
flowchart LR
accTitle: C++のマルチスレッド設計ベストプラクティスの知識マップ
accDescr: C++ではデータ競合がそのまま未定義動作になること、std::threadのデストラクタがstd::terminateを呼ぶ罠とjthreadによる解決、RAIIとscoped_lockによるロックの規律、atomicとvolatileの位置づけ、条件変数のスプリアスウェイクアップ、stop_tokenによる協調停止、Win32同期オブジェクトとの境界線を示す図
data_race_undefined_behavior["データ競合による未定義動作"]
shared_mutable_state["共有可変状態"]
race_condition["競合状態(race condition)"]
volatile_keyword["volatile"]
std_thread["std::thread"]
std_terminate["std::terminate"]
std_jthread["std::jthread"]
raii["RAII(Resource Acquisition Is Initialization)"]
std_stop_token["std::stop_token"]
cooperative_cancellation["協調キャンセル"]
std_condition_variable_any["std::condition_variable_any"]
std_async_future["std::async / std::future"]
parallel_overhead["並列化のオーバーヘッド"]
ppl_parallel_for["PPLのparallel_for / parallel_for_each"]
cpp_parallel_algorithms["C++17の並列アルゴリズム(std::execution::par)"]
std_scoped_lock["std::scoped_lock"]
deadlock["デッドロック(deadlock)"]
lock_ordering["ロック取得順序の統一"]
std_mutex["std::mutex"]
std_shared_mutex["std::shared_mutex"]
std_atomic["std::atomic"]
multi_variable_invariant["複数変数にまたがる不変条件"]
std_condition_variable["std::condition_variable"]
spurious_wakeup["スプリアスウェイクアップ"]
in_process_mutual_exclusion["プロセス内の相互排他"]
srw_lock["SRWロック(Slim Reader/Writer Lock)"]
win32_sync_object["Win32同期オブジェクト"]
dllmain_loader_lock["DllMainとローダーロック"]
postmessage_ui_dispatch["PostMessageによるUIスレッドへの依頼"]
ui_thread_context["UIスレッドのコンテキスト"]
shared_mutable_state -->|"原因になり得る"| race_condition
shared_mutable_state -->|"原因になり得る"| data_race_undefined_behavior
volatile_keyword -->|"用いるのは非推奨"| data_race_undefined_behavior
std_thread -->|"原因になり得る"| std_terminate
std_jthread -->|"の後継"| std_thread
std_jthread -->|"実装を担う"| raii
std_jthread -->|"利用する"| std_stop_token
std_stop_token -->|"実装を担う"| cooperative_cancellation
std_condition_variable_any -->|"利用する"| std_stop_token
std_async_future -.->|"原因になり得る"| parallel_overhead
ppl_parallel_for -.->|"原因になり得る"| parallel_overhead
cpp_parallel_algorithms -->|"原因になり得る"| std_terminate
std_scoped_lock -->|"実装を担う"| raii
std_scoped_lock -->|"防止する"| deadlock
lock_ordering -->|"防止する"| deadlock
std_scoped_lock -->|"で構成できる"| std_mutex
std_shared_mutex -.->|"防止する"| race_condition
std_atomic -->|"防止する"| race_condition
std_atomic -->|"用いるのは非推奨"| multi_variable_invariant
std_condition_variable -->|"前提とする"| std_mutex
std_condition_variable -->|"原因になり得る"| spurious_wakeup
std_mutex -->|"推奨される対応"| in_process_mutual_exclusion
srw_lock -->|"推奨される対応"| in_process_mutual_exclusion
win32_sync_object -->|"用いるのは非推奨"| in_process_mutual_exclusion
dllmain_loader_lock -->|"原因になり得る"| deadlock
std_thread -->|"両立しない"| dllmain_loader_lock
postmessage_ui_dispatch -.->|"推奨される対応"| ui_thread_context
概念間の関係(全27件)
図と同じ関係を文章でも列挙します。表示している文と機械可読な意味データ(RDFa)は同じ要素に載っています。確度が「確立した関係」のものは直接の関係として、「条件付きの関係」のものは成立条件つきの言明(rdf:Statement)として表現しています。
- 共有可変状態は競合状態(race condition)の原因になることがあります。
- 共有可変状態はデータ競合による未定義動作の原因になることがあります。
- volatileをデータ競合による未定義動作に用いることは推奨されません。
- std::threadはstd::terminateの原因になることがあります。
- std::jthreadはstd::threadの後継に当たります。
- std::jthreadはRAII(Resource Acquisition Is Initialization)の実装を担います。
- std::jthreadはstd::stop_tokenを利用します。
- std::stop_tokenは協調キャンセルの実装を担います。
- std::condition_variable_anyはstd::stop_tokenを利用します。
- std::async / std::futureは並列化のオーバーヘッドの原因になることがあります。
- PPLのparallel_for / parallel_for_eachは並列化のオーバーヘッドの原因になることがあります。
- C++17の並列アルゴリズム(std::execution::par)はstd::terminateの原因になることがあります。
- std::scoped_lockはRAII(Resource Acquisition Is Initialization)の実装を担います。
- std::scoped_lockはデッドロック(deadlock)を防ぎます。
- ロック取得順序の統一はデッドロック(deadlock)を防ぎます。
- std::scoped_lockはstd::mutexで構成できます。
- std::shared_mutexは競合状態(race condition)を防ぎます。
- std::atomicは競合状態(race condition)を防ぎます。
- std::atomicを複数変数にまたがる不変条件に用いることは推奨されません。
- std::condition_variableはstd::mutexを前提とします。
- std::condition_variableはスプリアスウェイクアップの原因になることがあります。
- std::mutexはプロセス内の相互排他に対する本記事の推奨です。
- SRWロック(Slim Reader/Writer Lock)はプロセス内の相互排他に対する本記事の推奨です。
- Win32同期オブジェクトをプロセス内の相互排他に用いることは推奨されません。
- DllMainとローダーロックはデッドロック(deadlock)の原因になることがあります。
- std::threadはDllMainとローダーロックと両立しません。
- PostMessageによるUIスレッドへの依頼はUIスレッドのコンテキストに対する本記事の推奨です。
主要概念の定義
機械可読データ
このデータセットについて
このページはサイトの知識グラフ(_data/knowledge/)から自動生成されています。誤りの指摘はお問い合わせからお願いします。