std :: shared_ptr объяснил безопасность потока - PullRequest
96 голосов
/ 03 февраля 2012

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

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

РЕДАКТИРОВАТЬ:

Псевдокод:

// 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 потоке другие потоки увидят только недавно созданный объект?

Ответы [ 3 ]

83 голосов
/ 30 ноября 2012

Как уже отмечали другие, вы правильно поняли относительно ваших первоначальных 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

34 голосов
/ 03 февраля 2012
  1. Правильно, shared_ptr s использует атомные приращения / уменьшения значения счетчика ссылок.

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

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

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

14 голосов
/ 05 ноября 2017

std::shared_ptr не является поточно-ориентированным.

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

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

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

...