Is boost:: property_tree:: ptree thread safe?
Я использую boosts read_json в нескольких потоках в куске кода. Ниже приведена упрощенная разбивка вызова. Я получаю segfaults в одном из потоков (а иногда и другого), и это заставляет меня думать, что read_json не является потокобезопасным (или я просто использую его глупо)
void someclass::dojson() {
using boost::property_tree::ptree;
ptree pt;
std::stringstream ss(json_data_string);
read_json(ss,pt);
}
Теперь json_data_string отличается от двух классов (это только json-данные, полученные по сокету).
Так что read_json потокобезопасен или мне нужно отключить его (вместо этого нет) или есть лучший способ вызова read_json, который является потокобезопасным?
Ответы
Ответ 1
Поскольку boost json parser зависит от boost:: spirit, а дух не является стандартом по умолчанию.
Вы можете добавить этот макрос перед любым заголовочным файлом ptree, чтобы его разрешить.
#define BOOST_SPIRIT_THREADSAFE
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
Ответ 2
TL; ДР:
Мое предложение: используйте идиому атомного свопа
ptree my_shared;
mutex shared_ptree_lock;
{
ptree parsed; // temporary
read_json(ss,pt); // this may take a while (or even fail)
lock_guard hold(shared_ptree_lock);
std::swap(pt, my_shared); // swap under lock
}
Теперь, нужно ли блокировать общее дерево перед чтением, зависит от ваших знаний о контексте потоковой передачи (другими словами, зависит, знаете ли вы, что ваше дерево может быть изменено одновременно).
Чтобы сделать вещи безумно гибкими, сделайте то же самое через shared_ptr<ptree>
, но это будет иметь значительные накладные расходы. Предположим, что с помощью идиомы свопа вам не придется блокировать вещи на стороне чтения, так как читатели с удовольствием продолжат читать старое дерево, и если они будут выполнены, чтение и выпуск shared_ptr
, оно будет уничтожено в конец.
Я не совсем уверен, чего вы ожидаете. Когда дерево свойств, доступное для записи из двух потоков, никогда не будет потокобезопасным без блокировки. Таким образом, я предполагаю, что вы имеете в виду, это дерево свойств threadsafe для чтения, одновременно анализируя его где-то еще.
Здесь мое основное ожидание: нет. С++ имеет культуру "платить за то, что вам нужно", вы не увидите нитевидных классов общего назначения. Будет вариант
- препроцессор #define для включения безопасности потоков
- параметр шаблона политики, который определяет поведение
Посмотрев на исходный код, удивительно, похоже, что он был практически потокобезопасным. Но не полностью:)
Похоже, что нет #define или флага для установки безопасного потока дерева свойств, поэтому вы застряли в блокировке.
Обоснование:
Глядя на internal_read_json
, я вижу, что он обращается к потоку (который должен быть закрыт для этого читателя в любом случае, поскольку совместное использование потоков для нескольких (одновременных) пользователей вряд ли когда-либо полезно 1) и то, очень правильно, только свопит корень ptree (pt
) node с деревом контекста парсера.
Очевидно, что функция атомного свопа в основном предназначена для безопасности исключений (вы не хотите менять ptree, если возникло исключение на половине сеанса JSON). Однако IFF-операция подкачки должна быть потокобезопасной, это также сделает доступ к pt
потокобезопасным.
Увы, на ptree_implementation мы видим, что swap не является потокобезопасным:
template<class K, class D, class C> inline
void basic_ptree<K, D, C>::swap(basic_ptree<K, D, C> &rhs)
{
m_data.swap(rhs.m_data);
// Void pointers, no ADL necessary
std::swap(m_children, rhs.m_children);
}
Во-первых, у вас может быть условие гонки между обменом m_data
и m_children
, и более того, свопы являются стандартными, а не атомными свопами.
1 кроме istringstream
, очевидно, не является потокобезопасным, так как это стандартный класс библиотеки С++ 98
Ответ 3
Благодаря Вэй и ликвидному океану; #define исправил мою проблему. FYI Я получал эту трассировку стека из windbg! Анализирую -v, когда у меня было два потока, называющих read_json.
10 07b3e4fc 0021b2de sseng! _STL:: for_each < _STL:: reverse_iterator, _STL:: allocator > , _ STL:: basic_string, _STL:: allocator > , _ STL:: less < _STL:: basic_string, _STL:: allocator → → , boost:: spirit:: classic:: parser_context → * * > , _ STL:: binder2nd < _STL:: mem_fun1_t, _STL:: allocator > , _ STL:: basic_string, _STL:: allocator > , _ STL:: less < _STL:: basic_string, _STL:: allocator → → , boost:: spirit:: classic:: parser_context → , boost:: spirit:: classic:: grammar, _STL:: allocator > , _ STL:: basic_string, _STL:: allocator > , _ STL:: less < _STL:: basic_string, _STL:: allocator → → , boost:: spirit:: classic:: parser_context > * → > + 0x11 [c:\ss\tp\aoo341\main\stlport\rel\inc\stlport\stl_algo.h @65]
11 07b3e520 0021f867 sseng! Boost:: spirit:: classic:: impl:: grammar_destruct, _STL:: allocator > , _ STL:: basic_string, _STL:: allocator > , _ STL:: less < _STL:: basic_string, _STL:: allocator → → , boost:: spirit:: classic:: parser_context → + 0x28 [c:\ss\tp\aoo341\main\boost\rel\inc\boost\spirit\home\classic\core\non_terminal\impl\grammar.ipp @325]
12 07b3e54c 002224fa sseng! Boost:: spirit:: classic:: grammar, _STL:: allocator > , _ STL:: basic_string, _STL:: allocator > , _ STL:: less < _STL:: basic_string, _STL:: allocator → → , boost:: spirit:: classic:: parser_context > :: ~ grammar, _STL:: allocator > , _ STL:: basic_string, _STL:: allocator > , _ STL:: less < _STL:: basic_string, _STL:: allocator → → , boost:: spirit:: classic:: parser_context > + 0x1e [c:\ss\tp\aoo341\main\boost\rel\inc\boost\spirit\home\classic\core\non_terminal\grammar.hpp @52]
13 07b3e574 00226e37 sseng! Boost:: property_tree:: json_parser:: json_grammar, _STL:: allocator > , _ STL:: basic_string, _STL:: allocator > , _ STL:: less < _STL:: basic_string, _STL:: allocator → → :: ~ json_grammar, _STL:: allocator > , _ STL:: basic_string, _STL:: allocator > , _ STL:: less < _STL:: basic_string, _STL:: allocator → → + 0x28
14 07b3e784 00226f5c sseng! Boost:: property_tree:: json_parser:: read_json_internal, _STL:: allocator > , _ STL:: basic_string, _STL:: allocator > , _ STL:: less < _STL:: basic_string, _STL:: allocator → → + 0x149 [c:\ss\tp\aoo341\main\boost\rel\inc\boost\property_tree\detail\json_parser_read.hpp @317]
15 07b3e7c0 00232261 sseng! Boost:: property_tree:: json_parser:: read_json, _STL:: allocator > , _ STL:: basic_string, _STL:: allocator > , _ STL:: less < _STL:: basic_string, _STL:: allocator → → + 0x25 [c:\ss\tp\aoo341\main\boost\rel\inc\boost\property_tree\json_parser.hpp @45]
16 07b3ea20 00232a28 sseng! SSPhone:: Рукопожатие + 0x15b [c:\ss\xl\src\cpp\bin\eng\ssphone.cpp @272]
17 07b3ea5c 00234fc7 sseng! SSPhone:: OnEvent + 0x1a9 [c:\ss\xl\src\cpp\bin\eng\ssphone.cpp @232]
18 07b3fb7c 6f6b3433 sseng! PhoneThreadFunc + 0x1ed [c:\ss\xl\src\cpp\bin\eng\ssthrd.cpp @198]
Ответ 4
Парсер JSON в ptree был переписан и выпущен в Boost 1.59. Добавление BOOST_SPIRIT_THREADSAFE
define больше не требуется для дерева свойств, поскольку оно больше не использует Boost.Spirit
, а функция read_json
может считаться потокобезопасной.