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

Ros*_*s W 18 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);
 }
Run Code Online (Sandbox Code Playgroud)

现在json_data_string在两个类之间是不同的(它只是通过套接字接收的json数据).

那么read_json线程是安全的还是我必须互斥它(而不是)或者是否有更好的方法来调用线程安全的read_json?

小智 19

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

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

#define BOOST_SPIRIT_THREADSAFE
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
Run Code Online (Sandbox Code Playgroud)

  • 如果你没有全局定义它,而是在多个独立的 cpp 中使用 Spirit/json_parser,它仍然会崩溃,因为烦人的是 Sprit 有一个静态变量,它将在它们之间共享。 (2认同)

seh*_*ehe 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
}
Run Code Online (Sandbox Code Playgroud)

现在,您是否需要在读取之前锁定共享树,取决于您对线程上下文的了解(换句话说,取决于您是否知道您的树可以同时被修改).

为了使事情变得非常灵活,做同样的事情shared_ptr<ptree>- 但这会带来相当大的开销.前者是,使用交换习语,你不必在阅读方面锁定东西,因为读者会愉快地继续读取旧树,如果它们完成阅读并释放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);
}
Run Code Online (Sandbox Code Playgroud)

首先,你可以有交换之间的竞争条件m_datam_children,进一步,掉期是标准的,不是原子互换.


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

  • 嗯,所以我做了一个回溯,看起来read_json正在使用boost :: spirit.我四处搜索,发现如果使用精神,你应该#define BOOST_SPIRIT_THREADSAFE.我把它放在两个线程类的头文件中,并使用-mt库重新编译(我认为这些在linux上没有区别).无论如何 - 没有分裂5分钟,比以前长了很多.会看到它是怎么回事. (11认同)