【问题标题】:is boost::property_tree::ptree thread safe?boost::property_tree::ptree 线程安全吗?
【发布时间】:2011-12-30 17:07:23
【问题描述】:

我在一段代码的几个线程中使用 boosts 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 是线程安全的?

【问题讨论】:

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


    【解决方案1】:

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

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

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

    【讨论】:

    • 如果你没有在全局范围内定义它,但在多个独立的 cpps 中使用了 Spirit/json_parser,它仍然会崩溃,因为恼人的 Sprit 有一个静态变量将在它们之间共享。跨度>
    【解决方案2】:

    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&lt;ptree&gt; 执行相同的操作-但这将承担相当大的开销。前提是,使用交换习语,您不必在读取端锁定东西,因为读者会很高兴地继续阅读旧树,如果他们完成阅读并释放shared_ptr,它将被销毁结束。


    我不完全确定您的期望。从两个线程访问属性树以进行写入,如果没有锁定,永远不会是线程安全的。因此,我假设您的意思是,属性树在读取时是线程安全的,同时在其他地方解析它。

    在这里,我的主要期望是:不。 C++ 有一种“按需付费”的文化,你不会看到任何线程安全的通用类。会有

    的选项
    • 一个预处理器#define 来开启线程安全
    • 管理行为的策略模板参数

    查看源代码后,令人惊讶的是,它看起来好像是几乎线程安全的。但不完全:)

    似乎没有设置#define 或标志来使属性树线程安全,所以你被锁定了。

    理由:

    看着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 之间存在竞争条件,此外,交换是标准的,而不是原子交换。


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

    【讨论】:

    • 是的,我实际上是在尝试从两个单独的线程(在独立数据上)使用 read_json 函数。我猜他们在内存中调用相同的函数(净一个单独的实例),因此是段错误 - 该死的..
    • @RossW:听起来很困惑。函数必须相同(只有一个二进制),但它们不能相互干扰(它们位于不同的堆栈、不同的线程上)。冲突由对输入流或 pt 输出参数的访问引起。您需要在共享它们时同步对它们的访问。更重要的是,您将不得不考虑生命周期(旧树的破坏将使您对其内部的任何指针/引用无效)。
    • 我也是这么想的,这也是我对此感到困惑的原因。我将尝试简化版本的代码,看看我得到了什么
    • 嗯好的,所以我做了一个回溯,看起来 read_json 正在使用 boost::spirit。我四处搜索,发现如果使用精神,你应该#define BOOST_SPIRIT_THREADSAFE。我已将它放在两个线程类的头文件中,并使用 -mt 库重新编译(我认为这些在 linux 上没有什么不同)。无论如何 - 5 分钟内没有出现段错误,这比以前长了很多。看看情况如何。
    • @Ross W:感谢您的指点。我处于相同的情况(多个线程,要解析的完全独立的数据)并且在实例化精神语法时看到了类似的崩溃。使用#define,现在一切正常。
    【解决方案3】:

    ptree 中的 JSON 解析器已在 Boost 1.59 中重写并发布。属性树不再需要添加BOOST_SPIRIT_THREADSAFE 定义,因为它不再使用Boost.Spirit 并且read_json 函数可能被认为是线程安全的。

    【讨论】:

    • 能否请您指出说明这一点的官方 boost 文档。我检查了boost.org/users/history/version_1_59_0.html。那里提到了在属性树中使用了一个新的 JSON 解析器,但你能指出他们说它不使用 Boost.Spirit 的位置吗
    【解决方案4】:

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

    10 07b3e4fc 0021b2de sseng!_STL::for_each<_STL::reverse_iterator<boost::spirit::classic::impl::grammar_helper_base<boost::spirit::classic::grammar<boost::property_tree::json_parser::json_grammar<boost::property_tree::basic_ptree<_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> >,_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> >,_STL::less<_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> > > > >,boost::spirit::classic::parser_context<boost::spirit::classic::nil_t> > > * *>,_STL::binder2nd<_STL::mem_fun1_t<int,boost::spirit::classic::impl::grammar_helper_base<boost::spirit::classic::grammar<boost::property_tree::json_parser::json_grammar<boost::property_tree::basic_ptree<_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> >,_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> >,_STL::less<_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> > > > >,boost::spirit::classic::parser_context<boost::spirit::classic::nil_t> > >,boost::spirit::classic::grammar<boost::property_tree::json_parser::json_grammar<boost::property_tree::basic_ptree<_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> >,_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> >,_STL::less<_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> > > > >,boost::spirit::classic::parser_context<boost::spirit::classic::nil_t> > *> > >+0x11 [c:\ss\tp\aoo341\main\stlport\rel\inc\stlport\stl\_algo.h @ 65]
    11 07b3e520 0021f867 sseng!boost::spirit::classic::impl::grammar_destruct<boost::spirit::classic::grammar<boost::property_tree::json_parser::json_grammar<boost::property_tree::basic_ptree<_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> >,_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> >,_STL::less<_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> > > > >,boost::spirit::classic::parser_context<boost::spirit::classic::nil_t> > >+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<boost::property_tree::json_parser::json_grammar<boost::property_tree::basic_ptree<_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> >,_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> >,_STL::less<_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> > > > >,boost::spirit::classic::parser_context<boost::spirit::classic::nil_t> >::~grammar<boost::property_tree::json_parser::json_grammar<boost::property_tree::basic_ptree<_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> >,_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> >,_STL::less<_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> > > > >,boost::spirit::classic::parser_context<boost::spirit::classic::nil_t> >+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<boost::property_tree::basic_ptree<_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> >,_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> >,_STL::less<_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> > > > >::~json_grammar<boost::property_tree::basic_ptree<_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> >,_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> >,_STL::less<_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> > > > >+0x28
    14 07b3e784 00226f5c sseng!boost::property_tree::json_parser::read_json_internal<boost::property_tree::basic_ptree<_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> >,_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> >,_STL::less<_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> > > > >+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<boost::property_tree::basic_ptree<_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> >,_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> >,_STL::less<_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> > > > >+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]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-12-30
      • 2013-07-14
      • 1970-01-01
      • 2012-03-05
      • 2023-03-29
      • 1970-01-01
      • 2015-03-19
      • 2013-05-20
      相关资源
      最近更新 更多