Надежно ли: boost :: property_tree :: ptree? - PullRequest
18 голосов
/ 16 ноября 2011

Я использую Boost 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Поток безопасен?

Ответы [ 4 ]

17 голосов
/ 28 февраля 2014

Поскольку boost json parser зависит от boost :: spirit, а spirit не является потокобезопасным по умолчанию.

Вы можете добавить этот макрос перед любым заголовочным файлом ptree, чтобы разрешить его.

#define BOOST_SPIRIT_THREADSAFE
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
6 голосов
/ 16 ноября 2011

TL; DR:

Мое предложение: используйте идиому атомарного свопа

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, оно будет уничтожено вконец.


Я не совсем уверен, что вы ожидаете.Доступ к дереву свойств для записи из двух потоков никогда не станет поточно-безопасным без блокировки.Поэтому, я предполагаю, что вы имеете в виду, является ли дерево свойств безопасным для чтения и одновременно обрабатывает его где-то еще.

Здесь мое основное ожидание: нет.В C ++ есть культура «плати за то, что тебе нужно», ты не увидишь никаких поточно-ориентированных классов общего назначения.Будет иметь возможность

  • препроцессор #define для включения безопасности потока
  • параметр шаблона политики, который управляет поведением

Удивительно, но, посмотрев на исходный код, он выглядит так, как будто он почти потокобезопасен.Но не полностью:)

Похоже, что не существует #define или флага, который можно установить для обеспечения безопасности потока дерева свойств, поэтому вы застряли с блокировкой.

Обоснование:

Глядя на internal_read_json Я вижу, что он получает доступ только к потоку (который в любом случае должен быть закрыт для этого читателя, поскольку совместное использование потоков несколькими (одновременными) пользователями вряд ли когда-либо пригодится 1 ), а затем,очень правильно, только заменяет корневой узел ptree (pt) на дерево контекста синтаксического анализатора.

Очевидно, что функция атомарного свопинга в основном существует для исключительной безопасности (вы не хотите менять свое ptree, еслина полпути разбор JSON произошла исключительная ситуация).Тем не менее, IFF операция подкачки должна была быть поточно-ориентированной, это также сделало бы доступ к pt потоко-безопасным.

Увы, на ptree_implementation мы видим, что свопне является потокобезопасным:

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, очевидно, не является поточно-ориентированным, поскольку это стандартная библиотека класса C ++ 98

0 голосов
/ 23 ноября 2017

Парсер JSON в ptree был переписан и выпущен в Boost 1.59. Добавление BOOST_SPIRIT_THREADSAFE define больше не требуется для дерева свойств, поскольку оно больше не использует Boost.Spirit, а функцию read_json можно считать поточно-безопасной.

0 голосов
/ 18 ноября 2016

Благодаря Wei и Liquidblueocean;#define исправил мою проблему.К вашему сведению, я получил эту трассировку стека из 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 :: грамматика, _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>>>>, повышение:: дух :: классический :: 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 :: грамматика, _STL :: allocator>, _ STL :: basic_string, _STL :: allocator>, _ STL :: less <_STL :: basic_string, _STL :: allocator>>>>, boost :: spirit ::classic :: parser_context> :: ~ грамматика, _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 ссенг!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]

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...