2016-12-03 7 views
0

私はintを読み込むだけのスレッドAとインクリメントするスレッドBを持っています。同期なしで同時にインクリメントされているintを読み出すことは安全ですか?

スレッドAを値の正確な最新情報にする必要はありません。実際には9になると8になります。ループであり、ある時点で9になるので大丈夫です。

問題は、現在のインデックス(スレッドAによって最後に読み込まれた位置)と配列の「サイズ」が格納されている別のintを格納する固定サイズの配列です。両方のint値が0から開始され、更新スレッドAは、サイズintがインデックスintより大きいかどうかを調べます。そうであれば、それはキャッチアップして新しい値を読み取ります。

正確な精度が必要ないため、同期を回避できるかどうかを知りたいと思います。

+0

同期を行わないと結果が指定されません。実際には、私はあなたがこの場合には8または9を得ると期待しています。しかしそれは何でもよい。その結果、C++仕様には、42、-17、または真空中の光の速度を得ることを禁止するものは何もありません。結果は何でもよい。 –

+0

もっと実用的には、intがレジスタの少なくとも1つに格納されていればどうなるか考えてください。あなたは絶対に安全であるために必要な数の同期から始め、それがボトルネックであると確信している場合にのみ微調整してください。 – eran

+0

あなたは間違いなく同期を避けることはできませんが、std :: atomic を使うことができます。これは増分が非常に高速です。フードの下には、非常に高速なCPUレベルのアトミック操作である[https://en.wikipedia.org/wiki/Fetch-and-add](fetch/add)の形式があります。 – qexyn

答えて

6

いいえ。動作は未定義です。せいぜい、あなたの「正確な正確さを必要としない」というのは、「無意味な価値を許容する」ことに変わります。

1つのスレッドがバイト境界を超えて値を増やしたとします。たとえば、8ビットのバイトを持つシステムでは255から256になります。インクリメントの前に、上位バイトは値0を持ちます。インクリメント後、下位バイトの値は0になります。ここで0の値を読み込むことは可能ですか?それはあなたがよく見ることができるからです。下位バイトを書き込んだ後で新しいバイトを書き込む前にスレッドを切り替えると、別のスレッドで両方のバイトがゼロとみなされます。

注記これは、同期が解決する種類の問題を感じるための手作業の引数です。それは良い習慣を示唆するためのものではありません。優れた実践には、結果がなくても結果を推測できるかどうかにかかわらず、同期が必要です。

+1

プログラムにデータ競合がある場合、その振る舞いは未定義です。未定義の動作は、無意味なデータの可能性をはるかに超えています。 [この素敵な記事](https://software.intel.com/en-us/blogs/2013/01/06/benign-data-races-what-c​​ould-possibly-go-wrong)は、結果はちょうど少し離れている値とは劇的に異なる場合があります。 –

+0

数値は32/64整数で、512を超えません。まだ無意味な値を生成できますか? – Percee

+1

@Percee - 最後の文章、特に最後の文章をもう一度読んでください。 Dietmarのコメントがリンクしている記事を読んでください。 –

0

は同時に同期

せずにインクリメントされているint型を読み取るために、それが安全であるいいえ、それは危険なのです。

競合状態には、少なくとも1つのライタースレッドが関係します。 (すべてのスレッドが単なるリーダースレッドであれば、競合状態はなく、ロックは必要ありません)。

あなたの場合、両方のライタースレッド(つまり共有リソースの変更)である2つのスレッドがあります。だから競合状態がある。スレッドセーフなためには、共有リソース(つまりカウンタ)へのロックを実装する必要があります。

+0

Um、インクリメントには書き込みと読み取りが含まれます。 –

+1

競合状態にはライターとリーダーがそれぞれ1つずつ必要であり、少なくとも2つのライターではありません。 – Mat

+0

@PeteBecker申し訳ありませんが私は質問を誤読 – artm

関連する問題