C LANGUAGE TECHNOLOGY

【C言語】アトミック型修飾子「_Atomic」の使い方

悩んでいる人

C言語のアトミック型修飾子「_Atomic」の使い方を教えて!

こういった悩みにお答えします.

本記事の信頼性

  • リアルタイムシステムの研究歴12年.
  • 東大教員の時に,英語でOS(Linuxカーネル)の授業.
  • 2012年9月~2013年8月にアメリカのノースカロライナ大学チャペルヒル校(UNC)コンピュータサイエンス学部で客員研究員として勤務.C言語でリアルタイムLinuxの研究開発.
  • プログラミング歴15年以上,習得している言語: C/C++PythonSolidity/Vyper,Java,Ruby,Go,Rust,D,HTML/CSS/JS/PHP,MATLAB,Assembler (x64,ARM).
  • 東大教員の時に,C++言語で開発した「LLVMコンパイラの拡張」,C言語で開発した独自のリアルタイムOS「Mcube Kernel」GitHubにオープンソースとして公開
  • 2020年1月~現在はアメリカのノースカロライナ州チャペルヒルにあるGuarantee Happiness LLCのCTOとしてECサイト開発やWeb/SNSマーケティングの業務.2022年6月~現在はアメリカのノースカロライナ州チャペルヒルにあるJapanese Tar Heel, Inc.のCEO兼CTO.
  • 最近は自然言語処理AIイーサリアムに関する有益な情報発信に従事.
    • (AI全般を含む)自然言語処理AIの論文の日本語訳や,AIチャットボット(ChatGPT,Auto-GPT,Gemini(旧Bard)など)の記事を50本以上執筆.アメリカのサンフランシスコ(広義のシリコンバレー)の会社でプロンプトエンジニア・マネージャー・Quality Assurance(QA)の業務委託の経験あり.
    • (スマートコントラクトのプログラミングを含む)イーサリアムや仮想通貨全般の記事を200本以上執筆.イギリスのロンドンの会社で仮想通貨の英語の記事を日本語に翻訳する業務委託の経験あり.

こういった私から学べます.

C言語を独学で習得することは難しいです.

私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.

私のキャパシティもあり,一定数に達したら終了しますので,今すぐ追加しましょう!

友だち追加

独学が難しいあなたは,元東大教員がおすすめするC言語を学べるオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!

本記事はC11規格のスレッドを理解していることを前提とします.

アトミック型修飾子「_Atomic」

_Atomicは,C言語のC11規格から採用されたアトミック型修飾子です.

ここで,アトミック操作とは,途中で割り込まれない原始的(アトミック)な操作のことです.

アトミック操作は,基本的にはハードウェアの専用命令を利用して実装されます.

_Atomicはstdatomic.h内で利用されています.

stdatomic.hは/usr/include以下にはなく,/usr/lib/gcc/x86_64-linux-gnu/XX/include/stdatomic.hにあります(XXばGCCのバージョン番号).

stdatomic.hの詳細は以下を見ると良いです.

それでは,stdatomic.hで定義されている列挙やマクロ,宣言されている関数を紹介していきます.

memory_order列挙型

memory_order列挙型は,通常の(ノンアトミックの)メモリ同期操作を指定し,操作の順序付けを提供します.

memory_orderの整数定数は以下の通りです.

  • memory_order_relaxed:メモリ同期なし
  • memory_order_consume:以下のmemory_order_acquireより弱い順序付けでのreadのメモリ同期あり(仕様検討中のため,一時的に非推奨)
  • memory_order_acquire :readのメモリ同期あり
  • memory_order_release:writeのメモリ同期あり
  • memory_order_acq_rel:read/write両方のメモリ同期あり
  • memory_order_seq_cst:read/write両方のメモリ同期ありとSequential Consistencyあり

_Atomicを利用したtypedefによる型定義

上記に_Atomicを利用したtypedefによる型定義を示します.

_Atomicは上記の型で利用します.

ATOMIC_VAR_INITマクロ

ATOMIC_VAR_INITマクロは,valueと初期化互換性のある型のアトミックオブジェクトの初期化に適したトークン列に展開されます.

例えば,atomic_int型では,以下のように利用します.

atomic_initマクロ

atomic_initマクロは,objが指すアトミックオブジェクトをvalueに初期化します.

Aがロックされたアトミック型の場合,objに関連するロックもatomic_initマクロの呼び出しの後に初期化されます.

kill_dependencyマクロ

kill_dependencyマクロは,yの値を返し,変数yの依存関係の連鎖を終了させます.

読み込んだ値に依存する式に対する順序を保証するmemory_order_consumeのメモリ順序において,値の依存性を断ち切り,最適化を許可します.

atomic_thread_fence関数

atomic_thread_fence関数は,アトミック操作に対する補完的なメモリフェンスを提供します.

詳細はC++ですが,こちらを読みましょう!

atomic_signal_fence関数

atomic_signal_fence関数は,同一スレッド内のシグナルハンドラ実行との間でのみ有効なメモリフェンスを発行します.

詳細はC++ですが,こちらを読みましょう!

atomic_is_lock_freeマクロ

atomic_is_lock_freeマクロは,objが指すオブジェクトがロックフリーであるかどうかを返します.

ATOMIC_FREEマクロ

ATOMIC_FREEマクロは,対応するアトミック型のロックフリー性を示します.

非ポインタ型については,符号付き,符号無しの両方に適用されます.

atomic_flag構造体/ATOMIC_FLAG_INITマクロ

atomic_flag構造体は,アトミックなフラグを管理するものです.

__GCC_ATOMIC_TEST_AND_SET_TRUEVAL == 1の場合は_Bool型,それ以外はunsigned char型でフラグを管理します.

ATOMIC_FLAG_INITマクロは,atomic_flag構造体を0に初期化するマクロです.

_explicitマクロ

ここで,末尾が_explicitでないマクロは,引数memory_orderをmemory_order_seq_cstとした対応する_explicit関数と同じ動作になります.

_explicitマクロは,引数memory_orderの値に応じてメモリに影響を与えます.

atomic_store/atomic_store_explicitマクロ

atomic_store/atomic_store_explicitマクロは,objectが指す値を希望する値でアトミックに置き換えます.

order引数は,memory_order_acquire,memory_order_consume,memory_order_acq_relを設定できません.

atomic_load/atomic_load_explicitマクロ

atomic_load/atomic_load_explicitマクロは,objectが指す値をアトミックに返します.

order引数はmemory_order_release,memory_order_acq_relを設定できません.

atomic_exchange/atomic_exchange_explicitマクロ

atomic_exchange/atomic_exchange_explicitマクロは,objectが指す値を希望する値でアトミックに置き換えます.

返り値は,効果の直前にobjectが指した値です.

atomic_compare_exchange_strong/atomic_compare_exchange_strong_explicit/atomic_compare_exchange_weak/atomic_compare_exchange_weak_explicitマクロ

atomic_compare_exchange_strong/atomic_compare_exchange_strong_explicit/atomic_compare_exchange_weak/atomic_compare_exchange_weak_explicitマクロは,アトミックにobjectが指す値とexpectedが指す値が等しいかどうかを比較し,真ならばobjectが指す値をdesiredで置き換え,偽ならばexpectedの値をobjectが指す値で更新します.

さらに,比較が真の場合,成功の値に従ってメモリに影響を与え,比較が偽の場合,失敗の値に従ってメモリに影響を与えます.

返り値は,比較の結果です.

atomic_compare_exchange_weak/atomic_compare_exchange_weak_explicitマクロは,偽りの失敗をすることがあります.

つまり,expectedとobjectが参照するメモリの内容が等しい場合でも0を返し,元々そこにあったのと同じメモリ内容をexpectedに格納してしまうことがあります.

PowerPCやARM64などLL/SC命令を提供するアーキテクチャにおいて,atomic_compare_exchange_weak/atomic_compare_exchange_weak_explicitマクロは,比較と交換がループ内にある場合,より良いパフォーマンスをもたらします.

比較と交換でループを必要としない場合は,atomic_compare_exchange_strong/atomic_compare_exchange_strong_explicitマクロが望ましいです.

atomic_fetchマクロ

atomic_fetchマクロは,算術演算(+,-)とビット演算(|,^,&)を行うものです.

これらの演算はすべて,任意のアトミック整数型のオブジェクトに適用可能です.

atomic_boolには適用できないことに注意して下さい.

objectが指す値を,objectが指す値と与えられたoperandに適用された計算の結果とアトミックに置き換えます.

アトミックに,効果の直前にobjectによって指される値を返します.

atomic_flag_test_and_set/atomic_flag_test_and_set_explicitマクロ

atomic_flag_test_and_set/atomic_flag_test_and_set_explicitマクロは,アトミックにテストしてフラグを立てます.

atomic_flag_clear/atomic_flag_clear_explicitマクロ

atomic_flag_clear/atomic_flag_clear_explicitマクロは,アトミックにフラグをクリアします.

orderは,memory_order_consume,memory_order_acquire,memory_order_acq_relを設定できません.

_Atomicのの使い方

_Atomicの使い方は以下になります.

C11規格のスレッドで2つのスレッドを作成し,atomic_fetch_add_explicitマクロでアトミックにインクリメントします.

実行結果は以下になります.

「count = 100000」になることがわかります.

-DNO_LOCKオプションをつけてコンパイルして実行した結果は以下になります.

「count = 72510」と100000にならないことがわかります.(タイミングによっては100000になることがあります.)

まとめ

C言語のアトミック型修飾子「_Atomic」の使い方を紹介しました.

アトミック操作は奥深いので,何度も読み込んできちんと理解しましょう!

C言語を独学で習得することは難しいです.

私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.

私のキャパシティもあり,一定数に達したら終了しますので,今すぐ追加しましょう!

友だち追加

独学が難しいあなたは,元東大教員がおすすめするC言語を学べるオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!

-C LANGUAGE, TECHNOLOGY
-, , , , , ,