Programming組込み/IoT開発者

C言語のvolatileキーワードの使用の詳細とは何ですか、特にマルチスレッドやハードウェアレジスタを扱う際に不適切に使用した場合に発生するエラーは何ですか?

Hintsage AIアシスタントで面接を突破

回答。

volatileキーワードは、変数がコンパイラにとって予期しない方法で変更される可能性があることをコンパイラに伝え(たとえば、ハードウェア、別のスレッド、または割り込みハンドラによって)、その値をキャッシュしたり最適化したりすることを禁止します。

使用される場所:

  • ハードウェアレジスタを操作する際。
  • 割り込みハンドラで変更される変数のために。
  • スレッド間の通信に使用される変数のために(ただし重要な点として:volatileは原子性や同期を保証しません!)。

使用例:

volatile int flag = 0; void handler() { flag = 1; // 割り込みハンドラ } void loop() { while (!flag) { // イベントを待つ } // ... }

volatileがない場合、コンパイラはループを無限に置き換えることができ(メモリからflagを読み込まない)、volatileを使用すると変数は毎回メモリから読み込まれます。


策略的な質問。

スレッド間の情報交換を正しく行うためにvolatileを使用するだけで十分ですか?

一般的な誤解は、volatileがスレッド間のメモリ同期を保証し、操作を原子性にすると思うことです。

正しい回答:

volatileはマルチスレッド環境におけるデータ競合から保護せず、メモリバリアを提供しません:これはコンパイラに対しアクセスを最適化しないように指示するだけです。正確性を保証するためには、必ず同期プリミティブ(mutexatomic操作など)を使用する必要があります。

// これは安全ではありません! volatile int ready = 0; void thread1() { data = 123; // データを書き込む ready = 1; // 別のスレッドに通知 } void thread2() { while (!ready); // イベントを待つ printf("data = %d\n", data); // データがまだ更新されていない可能性があります! }

歴史


マイコンを制御するプロジェクトでは、メインループと割り込みハンドラ間の通信のための変数がvolatileとして定義されていなかったため、ファームウェアが時々フリーズしました — フラグの変更が検知されなかったのです。


マルチスレッドサーバーでは、シグナルの交換のためにvolatileを使用し、それだけで十分だと考えていましたが、結果的に捕まえにくいバグに遭遇しました:時々、スレッドは古いデータを読み込んだり、全く一貫性のない状態を読むことがありました — 原子変数やmutexが必要でした。


ハードウェアレジスタの操作時のエラー:値をvolatileなしで読み書きしたため、コンパイラはアクセスを最適化し、新しいレジスタの値を完全に無視することになりました — 一部のコマンドは動作しませんでした。