Безопасен ли поток shared_ptr <XXX>?
У меня вопрос о boost::shared_ptr<T>
.
Есть много потоков.
using namespace boost;
class CResource
{
// xxxxxx
}
class CResourceBase
{
public:
void SetResource(shared_ptr<CResource> res)
{
m_Res = res;
}
shared_ptr<CResource> GetResource()
{
return m_Res;
}
private:
shared_ptr<CResource> m_Res;
}
CResourceBase base;
//----------------------------------------------
// Thread_A:
while (true)
{
//...
shared_ptr<CResource> nowResource = base.GetResource();
nowResource.doSomeThing();
//...
}
// Thread_B:
shared_ptr<CResource> nowResource;
base.SetResource(nowResource);
//...
Q1
Если Thread_A не волнует, что nowResource
является самым новым, будет ли эта часть кода иметь проблемы?
Я имею в виду, когда Thread_B не полностью SetResource()
, Thread_A получает неправильную интеллектуальную точку GetResource()
?
Q2
Что такое поточно-безопасное?
Если мне неинтересно, является ли ресурс новейшим, будет ли shared_ptr<CResource> nowResource
сбой программы при отпускании nowResource
или проблема будет уничтожена shared_ptr<CResource>
?
Ответы
Ответ 1
Из документа :
shared_ptr
объекты предлагают одинаковые уровень безопасности потока как встроенный типы. Пример shared_ptr
может быть "читать" (доступ к нему осуществляется только с константой операций) одновременно несколькими потоки. Различные shared_ptr
экземпляры могут быть "записаны в" (доступ с помощью изменяемых операций таких как operator=
или reset) одновременно несколькими потоками (даже если эти экземпляры являются копиями, и использовать один и тот же счетчик ссылок внизу.)
Любые другие одновременные обращения приводят к поведению undefined.
Таким образом, ваше использование небезопасно, поскольку оно использует одновременное чтение и запись m_res
. Пример 3 в дополнительной документации также иллюстрирует это.
Вы должны использовать отдельный mutex, который защищает доступ к m_res
в SetResource
/GetResource
.
Ответ 2
boost::shared_ptr<>
предлагает определенный уровень безопасности потоков. Счетчик ссылок обрабатывается безопасным потоком (если вы не настроите boost для отключения поддержки потоковой передачи).
Итак, вы можете скопировать shared_ptr
, и ref_count поддерживается правильно. То, что вы не можете сделать безопасно в нескольких потоках, это изменить фактический экземпляр объекта shared_ptr
непосредственно из нескольких потоков (например, вызвать reset()
на нем из нескольких потоков). Таким образом, ваше использование небезопасно - вы изменяете фактический экземпляр shared_ptr
в нескольких потоках - вам понадобится ваша собственная защита.
В моем коде shared_ptr
обычно являются локальными или параметрами, передаваемыми по значению, поэтому проблем нет. Получая их из одного потока в другой, я обычно использую потокобезопасную очередь.
Конечно, ничто из этого не касается безопасности потоков доступа к объекту, на который указывает shared_ptr
, который также зависит от вас.
Ответ 3
Ну, документация tr1:: shared_ptr (которая основана на boost) рассказывает о другой истории, которая подразумевает, что управление ресурсами является потокобезопасным, тогда как доступ к ресурсу не является.
"...
Безопасность потоков
Функции С++ 0x-only: поддержка rvalue-ref/move, поддержка распределителя, конструктор псевдонимов, make_shared и allocate_shared. Кроме того, конструкторы, принимающие параметры auto_ptr, устарели в режиме С++ 0x.
В разделе "Безопасность потока" документации Boost shared_ptr говорится, что "объекты shared_ptr обеспечивают тот же уровень безопасности потоков, что и встроенные типы". Реализация должна гарантировать, что параллельные обновления для отдельных экземпляров shared_ptr верны, даже если эти экземпляры совместно используют счетчик ссылок, например.
shared_ptr a (новый A);
shared_ptr b (a);
//Тема 1//Тема 2
a.reset(); b.reset();
Объект с динамическим распределением должен быть уничтожен ровно одним из потоков. Слабые ссылки делают вещи еще более интересными. Совместное состояние, используемое для реализации shared_ptr, должно быть прозрачным для пользователя, и инварианты должны быть сохранены в любое время. Ключевыми частями общего состояния являются сильные и слабые подсчеты ссылок. Обновления для них должны быть атомарными и видимыми для всех потоков, чтобы обеспечить правильную очистку управляемого ресурса (который, в конце концов, является задачей shared_ptr!). В многопроцессорных системах может потребоваться синхронизация памяти, чтобы обновления ссылок и уничтожение управляемого ресурса не могут участвовать в гонке.
... "
см
http://gcc.gnu.org/onlinedocs/libstdc++/manual/memory.html#std.util.memory.shared_ptr
Ответ 4
m_Res не является потокобезопасным, поскольку он одновременно читает/записывает,
вам нужна функция boost:: atomic_store/load, чтобы защитить ее.
//--- Example 3 ---
// thread A
p = p3; // reads p3, writes p
// thread B
p3.reset(); // writes p3; undefined, simultaneous read/write
Ответ 5
Добавьте, ваш класс имеет условие циклических ссылок; shared_ptr<CResource> m_Res
не может быть членом CResourceBase
. Вместо этого вы можете использовать weak_ptr
.