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.