Безопасно ли использовать auto_ptr для обмена объектами без блокировки в многопоточной среде?

У меня есть несколько структур данных, которые я выделяю в куче, которые редко изменяются, но требуют быстрого доступа для чтения. Одним из примеров может быть структура, размещенная в куче, к которой многие потоки обращаются очень часто только для чтения. Периодически эту структуру нужно переписывать, и чтобы избежать блокировки, я задавался вопросом, безопасно ли ее использовать auto_ptr в основном, чтобы потоки, получившие ссылку, могли продолжать обработку до тех пор, пока они не закончили, но позволяя автору записи сделать копию структуры, переписать ее и быстро поменять указатели на новые auto_ptr экземпляр структуры.

Я получил эту идею от CopyOnWriteArrayList в Java и надеется выполнить что-то аналогичное в C ++.

4

Решение

std::auto_ptr не имеет никаких гарантий безопасности потока при вызове неконстантного члена, такого как reset() как вы предлагаете. Более того, ни один не делает std::unique_ptr который вы должны рассмотреть в качестве замены auto_ptr как auto_ptr эффективно устарела.

std::shared_ptr делает предоставить такие гарантии безопасности.

На что вы обычно гарантированы shared_ptr является то, что счетчик ссылок управляется атомарным способом, что означает, что вы защищены от гонок данных при создании копии shared_ptrпри условии, что в настоящее время никто не изменяет shared_ptr в тот самый момент.

Рассмотрим следующий вариант использования для вашего shared_ptr, У вас есть глобальный shared_ptr<string> sharedString; который в настоящее время указывает на экземпляр std::string, Для многих потоков безопасно вызывать get() получить указатель на строку. Они также могут создать свой собственный общий указатель на него следующим образом: shared_ptr<string> myString = sharedString;при условии, что никто не меняет то, на что указывает общий указатель.

Теперь давайте вернемся и исправим состояние гонки, которое существует в примере. Когда приходит время изменить то, на что указывает глобальный общий указатель, поток может сделать его копию — это проблема, поскольку он может оставить копию в несогласованном состоянии. Итак, мы должны убедиться, что мы изменили и скопировали это атомарно.

К счастью для нас, shared_ptr предоставляет ряд специализаций атомарных операций C ++ 11: atomic_load, atomic_store, а также atomic_exchange.

При создании копии shared_ptrиспользовать atomic_loadи при обновлении shared_ptrиспользовать atomic_store,

Делать это таким образом важно по двум причинам.

  1. Операции atomic_ * обеспечивают поточно-ориентированный способ сделать копию общего указателя.
  2. Создание копии означает, что когда глобальный общий указатель изменяется, ваша копия по-прежнему указывает на старую строку, поэтому вашему коду не нужно беспокоиться об изменении данных, с которыми он работает, когда он этого не ожидает.

Теперь важно отметить, что использование потоковых операций на shared_ptr не обеспечивает никакой безопасности потока для типа, на который он указывает. Вы всегда должны позаботиться о том, чтобы указанный объект использовался в поточно-ориентированном режиме.

8

Другие решения

Вы можете переназначить auto_ptr на лету с помощью reset (), кстати, он уничтожает ранее указанный объект.

Тем не менее, auto_ptr устарела, так как unique_ptr имеет функцию swap (), которая, похоже, немного соответствует тому, что вы ищете.

Имейте в виду, что эти классы не потокобезопасный.

2