9

私は C++0x のサポートを試していますが、あるべきではないと思われる問題があります。主題が理解できないか、gcc にバグがあります。

最初は次のコードがxあり、y等しいです。スレッド 1 は常にx最初にインクリメントし、次にインクリメントしますy。どちらもアトミックな整数値なので、インクリメントはまったく問題ありません。スレッド 2 はxがより小さいかどうかをチェックyし、そうであればエラー メッセージを表示します。

このコードは時々失敗しますが、なぜですか? ここでの問題はおそらくメモリの並べ替えですが、すべてのアトミック操作はデフォルトで順次一貫しており、これらの操作を明示的に緩和しませんでした。このコードを x86 でコンパイルしていますが、私が知る限り、順序付けの問題はありません。何が問題なのか説明してもらえますか?

#include <iostream>
#include <atomic>
#include <thread>

std::atomic_int x;
std::atomic_int y;

void f1()
{
    while (true)
    {
        ++x;
        ++y;
    }
}

void f2()
{
    while (true)
    {
        if (x < y)
        {
            std::cout << "error" << std::endl;
        }
    }
}

int main()
{
    x = 0;
    y = 0;

    std::thread t1(f1);
    std::thread t2(f2);

    t1.join();
    t2.join();
}

結果はこちらでご覧いただけます

4

4 に答える 4

12

比較には次の問題があります。

x < y

部分式 (この場合、xy) の評価の順序は指定されていないため、のy前に評価される場合もあれば、 の前に評価されるx場合xもありますy

xが最初に読まれる場合、問題があります:

x = 0; y = 0;
t2 reads x (value = 0);
t1 increments x; x = 1;
t1 increments y; y = 1;
t2 reads y (value = 1);
t2 compares x < y as 0 < 1; test succeeds!

yが最初に読み取られることを明示的に確認すると、問題を回避できます。

int yval = y;
int xval = x;
if (xval < yval) { /* ... */ }
于 2010-10-25T06:20:25.543 に答える
11

問題はテストにある可能性があります:

if (x < y)

スレッドは評価される可能性があり、かなり後になるまでx評価に取り掛かることができません。y

于 2010-10-25T06:03:19.833 に答える
4

時々、が 0 に戻るx直前に 0 にy戻ります。この時点でy、合法的に より大きくなりxます。

于 2010-10-25T08:16:51.107 に答える
-3

まず、「Michael Burr」と「James McNellis」に同意します。あなたのテストは公平ではありません。不合格になる可能性は十分にあります。ただし、「James McNellis」が示唆する方法でテストを書き直しても、テストは失敗する可能性があります。

これの最初の理由は、volatileセマンティクスを使用していないため、コンパイラがコードを最適化する可能性があるためです (シングルスレッドの場合は問題ないはずです)。

ただしvolatile、コードを使用しても、動作が保証されているわけではありません。

メモリの並べ替えの概念を完全には理解していないと思います。実際には、メモリの読み取り/書き込みの並べ替えは 2 つのレベルで発生する可能性があります。

  1. コンパイラは、生成された読み取り/書き込み命令の順序を交換する場合があります。
  2. CPU はメモリの読み書き命令を任意の順序で実行できます。

を使用volatileすることで(1)を防ぐことができます。ただし、(2)を防ぐために何もしていません-ハードウェアによるメモリアクセスの並べ替え。

これを防ぐには、特別なメモリ フェンス命令をコードに入れる必要があります (volatileコンパイラ専用とは異なり、CPU 用に指定されています)。

x86/x64 には、さまざまなメモリ フェンス命令があります。lockまた、デフォルトでセマンティクスを持つすべての命令は、フル メモリ フェンスを発行します。

詳細はこちら:

http://en.wikipedia.org/wiki/Memory_barrier

于 2010-10-25T07:02:15.323 に答える