Std:: shared_ptr безопасность потока объясняется

Я читаю http://gcc.gnu.org/onlinedocs/libstdc++/manual/shared_ptr.html, и некоторые проблемы безопасности потока все еще не ясны для меня:

  • Стандарт гарантирует, что подсчет ссылок обрабатывается поточно-безопасным и независимым от платформы, не так ли?
  • Аналогичная проблема - стандарт гарантирует, что только один поток (содержащий последнюю ссылку) вызовет удаление на общем объекте, правильно?
  • shared_ptr не гарантирует безопасность потока для объекта, хранящегося в нем.

EDIT:

Псевдокод:

// Thread I
shared_ptr<A> a (new A (1));

// Thread II
shared_ptr<A> b (a);

// Thread III
shared_ptr<A> c (a);

// Thread IV
shared_ptr<A> d (a);

d.reset (new A (10));

Вызов reset() в потоке IV удалит предыдущий экземпляр класса A, созданного в первом потоке, и заменит его новым экземпляром? Более того, после вызова reset() в IV потоке другие потоки будут видеть только вновь созданный объект?

Ответ 1

Как указывали другие, вы правильно поняли свои исходные 3 вопроса.

Но конечная часть вашего редактирования

Вызов reset() в потоке IV удалит предыдущий экземпляр класса A, созданного в первом потоке, и заменит его новым экземпляром? Более того, после вызова reset() в IV потоке другие потоки будут видеть только вновь созданный объект?

неверно. Только d будет указывать на новый A(10), а a, b и c будет продолжать указывать на оригинал A(1). Это ясно видно в следующем кратком примере.

#include <memory>
#include <iostream>
using namespace std;

struct A
{
  int a;
  A(int a) : a(a) {}
};

int main(int argc, char **argv)
{
  shared_ptr<A> a(new A(1));
  shared_ptr<A> b(a), c(a), d(a);

  cout << "a: " << a->a << "\tb: " << b->a
     << "\tc: " << c->a << "\td: " << d->a << endl;

  d.reset(new A(10));

  cout << "a: " << a->a << "\tb: " << b->a
     << "\tc: " << c->a << "\td: " << d->a << endl;

  return 0;                                                                                                          
}

(Ясно, что я не беспокоился ни о какой потоковой передаче: это не влияет на поведение shared_ptr::reset().)

Выход этого кода

a: 1 b: 1 c: 1 d: 1

a: 1 b: 1 c: 1 d: 10

Ответ 2

  • Исправить, shared_ptr использовать атомные приращения/декременты значения отсчета ссылок.

  • Стандарт гарантирует, что только один поток вызовет оператор удаления на общем объекте. Я не уверен, что он конкретно указывает, что последний поток, который удаляет его копию общего указателя, будет тем, который вызывает delete (вероятно, на практике это будет так).

  • Нет, они не могут, объект, хранящийся в нем, может быть одновременно отредактирован несколькими потоками.

РЕДАКТИРОВАТЬ: Незначительное отслеживание, если вы хотите понять, как работают общие указатели, вы можете посмотреть источник boost::shared_ptr: http://www.boost.org/doc/libs/1_37_0/boost/shared_ptr.hpp.

Ответ 3

std::shared_ptr не является потокобезопасным.

Общий указатель - это пара двух указателей, один на объект и один на блок управления (удерживая счетчик ссылок, ссылки на слабые указатели...).

Может быть несколько std :: shared_ptr, и всякий раз, когда они обращаются к блоку управления для изменения счетчика ссылок, он поточно-ориентирован, но сам std::shared_ptr НЕ является поточно-ориентированным или атомарным.

Если вы присваиваете новый объект std::shared_ptr когда другой поток использует его, он может закончиться указателем нового объекта, но при этом использовать указатель на управляющий блок старого объекта => CRASH.