简体   繁体   English

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

[英]is boost::property_tree::ptree thread safe?

I'm using boosts read_json in a couple of threads in a piece of code.我在一段代码的几个线程中使用 boosts read_json。 A simplified breakdown of the call is below.通话的简化细分如下。 I'm getting segfaults in one of the threads (and sometimes the other) and it's making me think that read_json is not thread safe (Or I'm just using it in a dumb way)我在其中一个线程(有时是另一个)中遇到段错误,这让我认为 read_json 不是线程安全的(或者我只是在以一种愚蠢的方式使用它)

void someclass::dojson() {
   using boost::property_tree::ptree;
   ptree pt;
   std::stringstream ss(json_data_string);

   read_json(ss,pt);
 }

Now json_data_string is different between the two classes (it's just json data received over a socket).现在 json_data_string 在两个类之间是不同的(它只是通过套接字接收的 json 数据)。

So is read_json thread safe or do I have to mutex it (rather not) or is there a better way of calling read_json that is thread safe?那么 read_json 线程安全还是我必须互斥它(而不是)或者有更好的调用 read_json 线程安全的方法吗?

Because boost json parser depends on boost::spirit, and spirit is not thread-safety default.因为 boost json 解析器依赖于 boost::spirit,而 Spirit 不是线程安全的默认值。

You can add this macro before any ptree header file to resolve it.你可以在任何 ptree 头文件之前添加这个宏来解决它。

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

TL;DR:特尔;博士:

My suggestion: use the atomic swap idiom我的建议:使用原子交换习语

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
}

Now, whether you need to lock the shared tree before reading, depends on your knowledge of the threading context (in other words, depends whether you know your tree can be being modified at the same time).现在,在阅读之前是否需要锁定共享树,取决于您对线程上下文的了解(换句话说,取决于您是否知道您的树可以同时被修改)。

To make things insanely flexible, do the same through shared_ptr<ptree> - but this will bear considerable overhead.为了让事情变得非常灵活,通过shared_ptr<ptree>做同样的事情 - 但这将承担相当大的开销。 The pre is, that with the swap idiom, you won't have to lock things on the read side, since readers will happily continue reading the old tree, and if they are done reading and release the shared_ptr it will get destroyed in the end.前提是,使用交换习惯用法,您不必在读取端锁定内容,因为读者会很高兴地继续读取旧树,如果他们完成读取并释放shared_ptr它将最终被销毁.


I'm not completely sure what you expect.我不完全确定你的期望。 With the property tree being accessed for writing from two threads is never going to be thread-safe without locking.由于从两个线程访问属性树以进行写入,没有锁定就永远不会是线程安全的。 Therefore, I assume that you mean, is property tree threadsafe for reading while simultaneously parsing it somewhere else.因此,我假设您的意思是,属性树是线程安全的,可以在读取的同时在其他地方解析它。

Here, my primary expectation is: no.在这里,我的主要期望是:不。 C++ has a culture of 'pay-for-what-you-need' you won't see any thread-safe general-purpose classes. C++ 具有“按需付费”的文化,您将看不到任何线程安全的通用类。 There would be the option of会有这样的选择

  • a preprocessor #define to switch on thread-safety一个预处理器 #define 来开启线程安全
  • a policy template parameter that govern's the behaviour管理行为的策略模板参数

After looking at the source code, surprisingly, it looks as if it was almost thread safe.查看源代码后,令人惊讶的是,它看起来几乎是线程安全的。 But not fully :)但不完全:)

It appears that there is no #define or flag to set to make property tree thread safe, so you're stuck with locking.似乎没有设置 #define 或标志来使属性树线程安全,因此您无法使用锁定。

Rationale:理由:

Looking at internal_read_json I see that it only accesses the stream (which should be private to this reader anyway, as sharing streams across multiple (concurrent) users is hardly ever useful 1 ), and then, very correctly, only swaps the ptree's ( pt ) root node out with the parser's context tree.查看internal_read_json我看到它只访问流(无论如何应该是这个读者私有的,因为在多个(并发)用户之间共享流几乎没有用1 ),然后,非常正确,只交换 ptree 的( pt )根节点与解析器的上下文树。

Obviously, the atomic swap functionality is mainly there for exception safety (you don't want to change your ptree if an exception occurred halfway parsing the JSON).显然,原子交换功能主要用于异常安全(如果在解析 JSON 的中途发生异常,您不想更改 ptree)。 However, IFF the swap operation were to be thread-safe, this would also make the access to pt thread-safe.但是, IFF交换操作是线程安全的,这也将使对pt的访问成为线程安全的。

Alas, onto the ptree_implementation, we see that the swap is not threadsafe:唉,在 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);
}

For one, you could have a race condition between swapping m_data and m_children , further more, the swaps are standard, not atomic swaps.一方面,您可能在交换m_datam_children之间存在竞争条件,此外,交换是标准的,而不是原子交换。


1 besides istringstream obviously not being thread-safe as it is a C++98 standard library class 1除了istringstream显然不是线程安全的,因为它是 C++98 标准库类

JSON parser in ptree has been rewritten and released in Boost 1.59. ptree 中的 JSON 解析器已在 Boost 1.59 中重写和发布。 Adding BOOST_SPIRIT_THREADSAFE define is no longer needed for Property Tree since it doesn't use Boost.Spirit anymore and read_json function may be considered thread safe.属性树不再需要添加BOOST_SPIRIT_THREADSAFE定义,因为它不再使用Boost.Spirit并且read_json函数可能被认为是线程安全的。

Thanks to Wei and liquidblueocean;感谢 Wei 和 Liquidblueocean; the #define fixed my issue. #define 解决了我的问题。 FYI I was getting this stack trace out of windbg !analyse -v when I had two threads calling read_json.仅供参考,当我有两个线程调用 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]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM