О потоковой безопасности weak_ptr
std::shared_ptr<int> g_s = std::make_shared<int>(1);
void f1()
{
std::shared_ptr<int>l_s1 = g_s; // read g_s
}
void f2()
{
std::shared_ptr<int> l_s2 = std::make_shared<int>(3);
std::thread th(f1);
th.detach();
g_s = l_s2; // write g_s
}
Что касается вышеприведенного кода, я знаю, что разные потоки, читающие и записывающие один и тот же shared_ptr
, приводят к условиям гонки. Но как насчет weak_ptr
? Есть ли какое-либо условие гонки в коде ниже? (Моя платформа - Microsoft VS2013.)
std::weak_ptr<int> g_w;
void f3()
{
std::shared_ptr<int>l_s3 = g_w.lock(); //2. here will read g_w
if (l_s3)
{
;/.....
}
}
void f4()
{
std::shared_ptr<int> p_s = std::make_shared<int>(1);
g_w = p_s;
std::thread th(f3);
th.detach();
// 1. p_s destory will motify g_w (write g_w)
}
Ответы
Ответ 1
Я знаю, что опаздываю, но это происходит при поиске "потока слабой_программы", и ответ Кейси - это не вся правда. Оба shared_ptr
и weak_ptr
могут использоваться из потоков без дальнейшей синхронизации.
Для shared_ptr
существует много документации (например, на cppreference.com или на fooobar.com/questions/71510/...). Вы можете безопасно получить доступ к shared_ptr
, которые указывают на один и тот же объект из разных потоков. Вы просто не можете ударить по одному указателю из двух потоков. Другими словами:
// Using p and p_copy from two threads is fine.
// Using p from two threads or p and p_ref from two threads is illegal.
std::shared_ptr<A> p = std::make_shared<A>();
std::shared_ptr<A> &p_ref = p;
std::shared_ptr<A> p_copy = p;
Чтобы решить эту проблему в вашем коде, перейдите g_s
в качестве параметра (по значению) * в f1()
.
Для слабых указателей гарантия безопасности скрыта в документации для weak_ptr:: lock:
Эффективно возвращает expired() ? shared_ptr<T>() : shared_ptr<T>(*this)
, выполняемый атомарно.
Вы можете использовать weak_ptr::lock()
, чтобы получить shared_ptr
из других потоков без дальнейшей синхронизации. Это также подтверждено здесь для Boost и этот ответ SO от Chris Jester-Young.
Опять же, вы должны не изменять один и тот же weak_ptr
из одного потока при обращении к нему из другого, поэтому передайте g_w
в f3()
по значению.
Ответ 2
shared_ptr
и weak_ptr
подпадают под одни и те же требования к требованиям к одежде, как и все другие стандартные типы библиотек: одновременные вызовы функций-членов должны быть потокобезопасными, если эти функции-члены не изменяются (const
) (Подробно в C + +11 §17.6.5.9 Избегание гонки данных [res.data.races]). Операторы присваивания, в частности, не const
.
Ответ 3
Для краткости в последующем обсуждении, различные weak_ptr
и shared_ptr
, которые сгенерированы из одного и того же исходного shared_ptr
или unique_ptr
будут называться "экземплярами". weak_ptr
и shared_ptr
, которые не разделяют один и тот же объект, не должны рассматриваться в данном анализе. Общие правила оценки безопасности потока:
- Одновременные вызовы функций-членов
const
в одном и том же экземпляре поточно-ориентированы. Все функции наблюдателя являются const
. - Одновременные вызовы в разных экземплярах являются поточно-ориентированными, даже если один из вызовов является модификатором.
- Одновременные вызовы в одном и том же экземпляре, когда хотя бы один из вызовов является модификатором, не являются потокобезопасными.
В следующей таблице показана безопасность потоков, когда два потока работают в одном и том же экземпляре одновременно.
+---------------+----------+-------------------------+------------------------+
| operation | type | other thread modifying | other thread observing |
+---------------+----------+-------------------------+------------------------+
| (constructor) | | not applicable | not applicable |
| (destructor) | | unsafe | unsafe |
| operator= | modifier | unsafe | unsafe |
| reset | modifier | unsafe | unsafe |
| swap | modifier | unsafe | unsafe |
| use_count | observer | unsafe | safe |
| expired | observer | unsafe | safe |
| lock | observer | unsafe | safe |
| owner_before | observer | unsafe | safe |
+---------------+----------+-------------------------+------------------------+
Обсуждение cppreference в std :: atomic (std :: weak_ptr) является наглядным в отношении безопасности одновременного доступа к различным экземплярам:
Обратите внимание, что управляющий блок, используемый std :: weak_ptr и std :: shared_ptr, является потокобезопасным: к различным неатомарным объектам std :: weak_ptr можно получить доступ с помощью изменяемых операций, таких как operator = или reset, одновременно несколькими потоками, даже когда эти экземпляры являются копиями или иным образом совместно используют один и тот же блок управления внутри.
С++ 20 вводит специализацию слабого указателя std::atomic
которая обеспечивает поточно-ориентированное изменение одного и того же экземпляра посредством соответствующей синхронизации. Обратите внимание, что когда дело доходит до конструкторов, инициализация из другого экземпляра не является атомарной. Например, atomic<weak_ptr<T>> myptr(anotherWeakPtr);
это не атомарная операция.