Подтвердить что ты не робот

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, который является потокобезопасным?

4b9b3361

Ответ 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 может считаться потокобезопасной.