Unique_ptrスレッドは安全ですか?以下のコードで同じ番号を2回出力することはできませんか?
#include <memory>
#include <string>
#include <thread>
#include <cstdio>
using namespace std;
int main()
{
unique_ptr<int> work;
thread t1([&] {
while (true) {
const unique_ptr<int> localWork = move(work);
if (localWork)
printf("thread1: %d\n", *localWork);
this_thread::yield();
}
});
thread t2([&] {
while (true) {
const unique_ptr<int> localWork = move(work);
if (localWork)
printf("thread2: %d\n", *localWork);
this_thread::yield();
}
});
for (int i = 0; ; i++) {
work.reset(new int(i));
while (work)
this_thread::yield();
}
return 0;
}
いいえ、スレッドセーフではありません。
両方のスレッドが明示的な同期なしで作業ポインターをmove
する可能性があるため、両方のスレッドが同じ値を取得するか、両方が無効なポインターを取得する可能性があります...これは未定義の動作です。
このようなことをしたい場合正しく、おそらくstd::atomic_exchange
のようなものを使用して、両方のスレッドが正しいセマンティクスで共有作業ポインターを読み取ったり変更したりできるようにする必要があります。
unique_ptrは、正しく使用するとスレッドセーフになります。あなたは不文律を破りました:あなたは参照によってスレッド間でunique_ptrを決して渡してはなりません。
Unique_ptrの背後にある哲学は、常に単一の(一意の)所有者がいるということです。そのため、同期しなくてもスレッド間でいつでも安全に渡すことができますが、参照ではなく値で渡す必要があります。 unique_ptrのエイリアスを作成すると、uniquenessプロパティが失われ、すべての賭けが無効になります。残念ながら、C++は一意性を保証できないため、信心深く従わなければならない慣習が残されています。 unique_ptrのエイリアスを作成しないでください!
Msdn によると:
次のスレッドセーフルールは、標準C++ライブラリのすべてのクラスに適用されます(以下で説明するshared_ptrクラスとiostreamクラスを除く)。
単一のオブジェクトは、複数のスレッドからの読み取りに対してスレッドセーフです。たとえば、オブジェクトAが与えられた場合、スレッド1とスレッド2から同時にAを読み取るのは安全です。
1つのオブジェクトが1つのスレッドによって書き込まれている場合、同じスレッドまたは他のスレッドでそのオブジェクトへのすべての読み取りと書き込みを保護する必要があります。たとえば、オブジェクトAが与えられた場合、スレッド1がAに書き込んでいる場合、スレッド2はAからの読み取りまたはAへの書き込みを防止する必要があります。
別のスレッドが同じタイプの別のインスタンスに対して読み取りまたは書き込みを行っている場合でも、あるタイプの1つのインスタンスに対して読み取りおよび書き込みを行っても安全です。たとえば、同じタイプのオブジェクトAとBが与えられた場合、Aがスレッド1で書き込まれ、Bがスレッド2で読み取られている場合は安全です。