19

私の理解が正しいことを確認したい。この種のことはトリッキーなので、何かが欠けているとほぼ確信しています。リアルタイム スレッドと非リアルタイム スレッドで構成されるプログラムがあります。非 RT スレッドが、RT スレッドによって使用されるメモリへのポインターをスワップできるようにしたい。

ドキュメントから、私の理解では、これは次の方法で実現できますg++

// global
Data *rt_data;

Data *swap_data(Data *new_data)
{
#ifdef __GNUC__
    // Atomic pointer swap.
    Data *old_d = __sync_lock_test_and_set(&rt_data, new_data);
#else
    // Non-atomic, cross your fingers.                                          
    Data *old_d = rt_data;
    rt_data = new_data;
#endif
    return old_d;
}

これは、(初期セットアップ以外の) プログラム内でrt_dataが変更される唯一の場所です。がrt_dataリアルタイム コンテキストで使用されると、ローカル ポインターにコピーされます。の場合old_d、後で古いメモリが使用されていないことが確認されると、非 RT スレッドで解放されます。これは正しいです?volatileどこか必要ですか?呼び出す必要がある他の同期プリミティブはありますか?

ちなみにC++でやってますが、Cだと答えが違うのか気になります。

ありがとうございます。

4

2 に答える 2

26

volatileで並行コードを記述する場合は、通常は使用しませんC/C++。のセマンティクスはvolatileあなたが望むものに非常に近いので魅力的ですが、最終的に volatileでは十分ではありません。残念ながらJava/C# volatile != C/C++ volatile。Herb Sutter は、紛らわしい混乱を説明する素晴らしい記事を書いています。

あなたが本当に欲しいのはメモリフェンスです。 __sync_lock_test_and_setフェンシングを提供します。

rt_data ポインターをローカル コピーにコピー (ロード) するときにも、メモリ フェンスが必要になります。

ロックフリーのプログラミングはトリッキーです。Gcc の c++0x 拡張機能を使用する場合は、少し簡単です。

#include <cstdatomic>

std::atomic<Data*> rt_data;

Data* swap_data( Data* new_data )
{
   Data* old_data = rt_data.exchange(new_data);
   assert( old_data != new_data );
   return old_data;
}

void use_data( )
{
   Data* local = rt_data.load();
   /* ... */
}
于 2010-03-19T16:03:06.243 に答える
3

更新volatile:変数へのアクセスが並べ替えられないことを保証するという事実が欠けているため、この答えは正しくありませんが、他の非アクセスおよび操作volatileに関してそのような保証は提供しません。volatileメモリフェンスはそのような保証を提供し、このアプリケーションに必要です。私の元の答えは以下にありますが、それに基づいて行動しないでください。次の誤った応答につながった私の理解の穴の適切な説明については、この回答を参照してください。

元の答え:

volatileはい、rt_data申告が必要です。変数にアクセスするスレッドの制御フロー外で変数を変更できる場合はいつでも、変数を宣言する必要がありますvolatilevolatileローカル ポインターにコピーしているので、せずに済むかもしれませんがvolatile、少なくともドキュメント化に役立ち、問題を引き起こす可能性のあるコンパイラーの最適化を抑制します。DDJから採用された次の例を考えてみましょう。

volatile int a;
int b;
a = 1;
b = a;

と の間でaの値を変更できる場合は、を宣言する必要があります(もちろん、 に古い値を割り当てることが許容される場合を除きます)。特にアトミックプリミティブを使用したマルチスレッドは、このような状況を構成します。この状況は、シグナル ハンドラーによって変更された変数や、奇数のメモリ位置 (ハードウェア I/O レジスタなど) にマップされた変数によってもトリガーされます。この質問も参照してください。a=1b=aavolatileb

そうでなければ、私には問題ないように見えます。

C では、おそらくGLibが提供するアトミック プリミティブを使用します。利用可能な場合はアトミック操作を使用し、アトミック操作が利用できない場合は、遅いが正しいミューテックスベースの実装にフォールバックします。Boost は、C++ にも同様のものを提供する場合があります。

于 2010-03-19T15:33:24.483 に答える