6

メモリ内の 2 つの値のアトミック合計が必要な状況に陥っています。私が継承したコードは次のようになります。

int a = *MemoryLocationOne;
memory_fence();
int b = *MemoryLocationTwo;
return (a + b) == 0;

a と b の個々の読み取りはアトミックであり、コード内のこれら 2 つのメモリ位置への書き込みもすべてロックレス アトミックです。ただし、問題は、2 つの場所の値が 2 つの読み取り間で変化する可能性があり、実際に変化することです。

では、この操作をアトミックにするにはどうすればよいでしょうか。私は CAS についてすべて知っていますが、読み取り-変更-書き込み操作をアトミックにすることだけが必要になる傾向があり、それは私がここでやりたいことではありません。

それを行う方法はありますか、またはコードをリファクタリングして、1つの値のみを確認する必要があるようにするための最良のオプションはありますか?

編集: ありがとう、最初のリビジョンでこれをロックレスでやりたいとは言いませんでしたが、2 回目のリビジョンの後でそれを理解した人もいました。こういうことを言う人は誰も信じないのはわかっていますが、実際にロックを使うことはできません。アトミックでミューテックスをエミュレートする必要があり、それはコードをリファクタリングして 2 つではなく 1 つの値を追跡するよりも手間がかかります。

今のところ、私の調査方法は、値が連続しているという事実を利用し、64 ビットの読み取りでアトミックにそれらを取得することです。これは、ターゲット プラットフォームではアトミックであることが保証されています。誰かが新しいアイデアを持っている場合は、貢献してください! ありがとう。

4

4 に答える 4

3

2 つの値のいずれかが読み取られたり書き込まれたりする場所はどこでも、メモリ バリア (ロックまたはクリティカル セクション) で囲まれていることを確認する必要があります。

// all reads...
lock(lockProtectingAllAccessToMemoryOneAndTwo)
{
    a = *MemoryLocationOne;
    b = *MemoryLocationTwo;
}

...

// all writes...
lock(lockProtectingAllAccessToMemoryOneAndTwo)
{
    *MemoryLocationOne = someValue;
    *MemoryLocationTwo = someOtherValue;
}
于 2009-04-10T00:40:28.787 に答える
3

aこのテスト中に と が変更されないようにする必要がある場合は、 と へのすべてのアクセスにb同じ同期を使用する必要があります。それがあなたの唯一の選択です。これらの値のいずれかに対する各読み取りおよび各書き込みは、同じメモリ フェンス、シンクロナイザー、セマフォ、タイムスライス ロック、または使用されるメカニズムを使用する必要があります。ab

これにより、次のことを確認できます。

memory_fence_start();
int a = *MemoryLocationOne;
int b = *MemoryLocationTwo;
int test = (a + b) == 0;
memory_fence_stop();

return test;

その後、a読んでいる間は変化しませんb。ただし、と へのすべてのアクセスには、同じ同期メカニズムを使用する必要があります。ab

ロックフリーの方法を探しているという質問への後の編集を反映するには、使用しているプロセッサと、その長さaと長さ、およびbこれらのメモリ位置が連続して適切に配置されているかどうかに完全に依存します。

これらがメモリ内で連続しており、それぞれが 32 ビットであり、プロセッサにアトミック 64 ビット読み取りがあると仮定すると、アトミック 64 ビット読み取りを発行して 2 つの値を読み取り、64 ビット値から 2 つの値を解析できます。 、計算を行い、返したいものを返します。"と同時に" へのアトミックな更新は不要で、" " または " " へのアトミックな更新のみを個別aに行う必要があると仮定すると、これはロックなしで必要なことを実行します。bab

于 2009-04-10T00:40:54.340 に答える
3

x86 をターゲットにしている場合は、64 ビットの比較/交換サポートを使用して、両方の int を 1 つの 64 ビット ワードにパックできます。

Windows では、次のようにします。

// Skipping ensuring padding.
union Data
{
     struct members
     {
         int a;
         int b;
     };

     LONGLONG _64bitData;  
};

Data* data;


Data captured;

do
{
    captured = *data;
    int result = captured.members.a + captured.members.b;
} while (InterlockedCompareExchange64((LONGLONG*)&data->_64bitData,
                    captured._64BitData,
                    captured._64bitData) != captured._64BitData);

本当に醜い。ロックを使用することをお勧めします-はるかに保守性が高くなります。

EDIT:個々の部分を更新して読むには:

data->members.a = 0;
fence();

data->members.b = 0;
fence();

int captured = data->members.a;

int captured = data->members.b;
于 2009-04-10T00:58:34.670 に答える
1

ロックなしでこれを行う方法は本当にありません。私の知る限り、二重のアトミック読み取りを備えたプロセッサはありません。

于 2009-04-10T00:44:03.257 に答える