4

これはおそらくばかげた質問ですが、次の疑似コードを検討してください。

struct Person {
    std::string name;
};

class Registry {
public:
  const std::string& name(int id) const {return _people[id].name;}
  void name(int id, const std::string& name) { [[scoped mutex]]; _people[id].name = name;}

private:
  std::map<int, Person> _people;
};

この簡単な例では、レジストリが複数のスレッドによってアクセスされるシングルトンであると想定しています。データを変更する操作中はロックしていますが、非変更アクセス中はロックしていません。

このスレッドは安全ですか、それとも読み取り操作中にロックする必要がありますか?複数のスレッドが同時にデータを変更しようとするのを防いでいますが、あるスレッドが別のスレッドが書き込みを同時に読み取ろうとした場合にどうなるかわかりません。

4

6 に答える 6

7

いずれかのスレッドがデータを変更できる場合は、すべてのアクセスに対してロックする必要があります。

そうしないと、「読み取り」スレッドの 1 つが不確定な状態のデータにアクセスする可能性があります。mapたとえば、 を変更するには、いくつかのポインターを操作する必要があります。mapすべてではありませんが一部が調整されている間に、読み取りスレッドがマップにアクセスする可能性があります。

データが変更されていないことを保証できる場合は、複数のスレッドからの複数の読み取りをロックする必要はありませんが、注意が必要な脆弱なシナリオが発生します。

于 2013-03-21T15:17:41.587 に答える
4

変更中にデータを読み取るのはスレッドセーフではありません。複数のスレッドが一度にデータを読み取ることは完全に安全です。

この違いが、リーダー/ライター ロックの目的です。それらは任意の数のリーダーを許可しますが、ライターがリソースをロックしようとすると、新しいリーダーは許可されなくなり、現在のすべてのリーダーが完了するまでライターはブロックされます。次に、ライターが続行し、完了すると、すべてのリーダーが再びアクセスを許可されます。

変更中にデータを読み取るのが安全ではない理由は、データが一貫性のない状態である可能性がある、またはそのように見える可能性があるためです (たとえば、オブジェクトが一時的に不変条件を満たさない可能性があります)。読者がその時点でそれを読むと、プログラムにバグがあり、データの一貫性を維持できていないのと同じです。

// example
int array[10];
int size = 0;

int &top() {
    return array[size-1];
}

void insert(int value) {
    size++;
    top() = value;
}

任意の数のスレッドをtop()同時に呼び出すことができますが、1 つのスレッドが実行されているinsert()場合、次のように行がインターリーブされると問題が発生します。

// thread calling insert         thread calling top
    size++;
                                   return array[size-1];
    array[size-1] = value

読み取りスレッドがガベージになります。

もちろん、これは問題が発生する可能性のある 1 つの方法にすぎません。一般に、異なるスレッドのコード行が単にインターリーブするかのようにプログラムが動作するとは想定できません。その仮定を有効にするために、言語は単にデータ競合 (つまり、これまで話してきたこと、オブジェクトを変更する少なくとも 1 つのスレッドで (非アトミック) オブジェクトにアクセスする複数のスレッド) を持つことができないことを伝えます。 *。

*完全を期すために; すべてのアトミック アクセスが、連続した一貫性のあるメモリ順序を使用すること。低レベルの作業をアトミック オブジェクトで直接行っているわけではないので、これは問題ではありません。

于 2013-03-21T15:18:02.880 に答える
3

このスレッドは安全ですか、それとも読み取り操作中にロックする必要がありますか?

スレッドセーフではありません。

C++11 標準のパラグラフ 1.10/4 によると:

2 つの式の評価は、一方がメモリ ロケーション (1.7) を変更し、もう一方が同じメモリ ロケーションにアクセスまたは変更する場合に競合します。

さらに、パラグラフ 1.10/21 によると:

プログラムの実行にデータ競合が含まれているのは、異なるスレッドに競合する 2 つのアクションが含まれており、そのうちの少なくとも 1 つがアトミックではなく、どちらも他のスレッドの前に発生しない場合です。このようなデータ競合は、未定義の動作を引き起こします。[...]

于 2013-03-21T15:19:12.957 に答える
0

スレッドセーフではありません。

読み取り操作は、要求されたオブジェクトを見つけるためにマップ (ツリー) を通過する可能性がありますが、書き込み操作は、マップから何かを突然追加または削除します (さらに悪いことに、イテレーターの実際のポイント)。

運が良ければ例外が発生します。そうでなければ、マップが一貫性のない状態にある間、未定義の動作になります。

于 2013-03-21T15:19:33.833 に答える
0

スレッドが読み込もうとしているときに別のスレッドが書き込みを行っていたらどうなるかわかりません。

誰も知らない。カオスが続くでしょう。

複数のスレッドが読み取り専用のリソースを共有できますが、誰かがそれを書き込もうとするとすぐに、書き込みが完了するまで誰もがアクセスできなくなります。

なんで?

書き込みはアトミックではありません。これらは、複数のクロック サイクルにわたって発生します。書き込まれたオブジェクトを読み込もうとするプロセスは、半分変更されたバージョンの一時的なガベージを見つける可能性があります。

そう

読み取りが書き込みと同時に行われることが予想される場合は、読み取りをロックします。

于 2013-03-21T15:19:34.037 に答える