是boost :: property_tree :: ptree线程安全吗?

时间:2011-11-16 18:44:57

标签: c++ json boost thread-safety segmentation-fault

我在一段代码中的几个线程中使用了boost read_json。简化的呼叫细分如下。我在其中一个线程(有时是另一个)中得到段错误,这让我觉得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 个答案:

答案 0 :(得分:17)

因为boost json解析器依赖于boost :: spirit,而spirit不是线程安全默认值。

您可以在任何ptree头文件之前添加此宏来解决它。

#define BOOST_SPIRIT_THREADSAFE
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>

答案 1 :(得分:6)

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或flag来使属性树线程安全,所以你一直处于锁定状态。

理由:

internal_read_json我看到它只访问流(无论如何都应该是这个读者专用的流,因为跨多个(并发)用户共享流几乎没有用 1 ) ,然后,非常正确,只使用解析器的上下文树交换ptree的(pt)根节点。

显然,原子交换功能主要用于异常安全(如果在解析JSON的过程中发生异常,则不希望更改ptree)。但是, 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_datam_children之间遇到竞争条件,此外,交换是标准的,而不是原子交换。


istringstream之外

1 显然不是线程安全的,因为它是C ++ 98标准库类

答案 2 :(得分:0)

感谢Wei和liquidblueocean; #define解决了我的问题。仅供参考我从windbg获取此堆栈跟踪!当我有两个线程调用read_json时,分析-v。

10 07b3e4fc 0021b2de sseng!_STL :: for_each&lt; _STL :: reverse_iterator,_STL :: allocator&gt;,_ STL :: basic_string,_STL :: allocator&gt ;, _ STL :: less&lt; _STL :: basic_string,_STL ::分配器&gt; &GT; &GT; &gt;,boost :: spirit :: classic :: parser_context&gt; &GT; * *&gt;,_ STL :: binder2nd&lt; _STL :: mem_fun1_t,_STL :: allocator&gt;,_ STL :: basic_string,_STL :: allocator&gt;,_ STL :: less&lt; _STL :: basic_string,_STL :: allocator&gt; &GT; &GT; &gt;,boost :: spirit :: classic :: parser_context&gt; &gt ;, boost :: spirit :: classic :: grammar,_STL :: allocator&gt;,_ STL :: basic_string,_STL :: allocator&gt;,_ STL :: less&lt; _STL :: basic_string,_STL :: allocator&gt; &GT; &GT; &gt;,boost :: spirit :: classic :: parser_context&gt; * GT; &GT; &gt; + 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&gt;,_ STL :: basic_string,_STL :: allocator&gt;,_ STL :: less&lt; _STL :: basic_string,_STL: :allocator&gt; &GT; &GT; &gt;,boost :: spirit :: classic :: parser_context&gt; &gt; + 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&gt;,_ STL :: basic_string,_STL :: allocator&gt;,_ STL :: less&lt; _STL :: basic_string,_STL :: allocator&gt ; &GT; &GT; &gt;,boost :: spirit :: classic :: parser_context&gt; ::〜语法,_STL :: allocator&gt;,_ STL :: basic_string,_STL :: allocator&gt;,_ STL :: less&lt; _STL :: basic_string,_STL :: allocator&gt; &GT; &GT; &gt;,boost :: spirit :: classic :: parser_context&gt; + 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&gt;,_ STL :: basic_string,_STL :: allocator&gt;,_ STL :: less&lt; _STL :: basic_string,_STL :: allocator&gt ; &GT; &GT; &gt; ::〜json_grammar,_STL :: allocator&gt;,_ STL :: basic_string,_STL :: allocator&gt;,_ STL :: less&lt; _STL :: basic_string,_STL :: allocator&gt; &GT; &GT; &GT; 0×28 + 14 07b3e784 00226f5c sseng!boost :: property_tree :: json_parser :: read_json_internal,_STL :: allocator&gt;,_ STL :: basic_string,_STL :: allocator&gt;,_ STL :: less&lt; _STL :: basic_string,_STL :: allocator&gt ; &GT; &GT; &gt; + 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&gt;,_ STL :: basic_string,_STL :: allocator&gt;,_ STL :: less&lt; _STL :: basic_string,_STL :: allocator&gt ; &GT; &GT; &gt; + 0x25 [c:\ ss \ tp \ aoo341 \ main \ boost \ rel \ inc \ boost \ property_tree \ json_parser.hpp @ 45] 16 07b3ea20 00232a28 sseng!SSPhone :: Handshake + 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]

答案 3 :(得分:0)

ptree中的JSON解析器已在Boost 1.59中重写并发布。 Property Tree不再需要添加BOOST_SPIRIT_THREADSAFE define,因为它不再使用Boost.Spiritread_json函数可能被认为是线程安全的。