Безопасен ли поток unique_ptr?
Безопасен ли поток unique_ptr? Невозможно ли для кода ниже печатать одинаковый номер дважды?
#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;
}
Ответы
Ответ 1
Нет, он не является потокобезопасным.
Оба потока могут потенциально move
указатель на работу без явной синхронизации, поэтому для обоих потоков можно получить одно и то же значение или оба получить недопустимый указатель... это поведение undefined.
Если вы хотите сделать что-то подобное правильно, вам, вероятно, нужно будет использовать что-то вроде std::atomic_exchange
, чтобы оба потока могли читать/изменять общий указатель рабочей области с помощью правильной семантики.
Ответ 2
unique_ptr является потокобезопасным при правильном использовании. Вы нарушили неписаное правило: никогда не пропускайте unique_ptr между потоками по ссылке.
Философия, лежащая в основе unique_ptr, заключается в том, что она имеет единственного (уникального) владельца во все времена. Из-за этого вы всегда можете безопасно передавать его между потоками без синхронизации - но вы должны передавать его по значению, а не по ссылке. Когда вы создаете псевдонимы для unique_ptr, вы теряете свойство уникальности, и все ставки отключены. К сожалению, С++ не может гарантировать уникальность, поэтому вы остаетесь с соглашением, согласно которому вы должны следовать религиозно. Не создавайте псевдонимы для unique_ptr!
Ответ 3
Согласно Msdn:
Следующие правила безопасности потока применяются ко всем классам Стандарта Библиотека С++ (кроме классов shared_ptr и iostream, как описано ниже).
Один объект является потокобезопасным для чтения из нескольких потоков. Для Например, учитывая объект A, безопасно читать A из потока 1 и из потока 2 одновременно.
Если один объект записывается одним потоком, то все считывает и запись в этот объект на том же или другом потоке должна быть защищенный. Например, если задан объект A, если поток 1 записывает A, то поток 2 должен быть запрещен для чтения или записи в A.
Безопасно читать и писать одному экземпляру типа, даже если другое поток читает или записывает в другой экземпляр того же типа. Например, если заданные объекты A и B того же типа, то безопасно, если A записывается в поток 1, а B читается в потоке 2.