c++ - es boost:: property_tree:: ptree thread seguro?
json thread-safety (4)
Estoy usando boosts read_json en un par de hilos en un fragmento de código. A continuación se muestra un desglose simplificado de la llamada. Estoy obteniendo seguridades en uno de los subprocesos (y, a veces, en el otro) y me hace pensar que read_json no es seguro para subprocesos (O simplemente lo estoy utilizando de forma tonta)
void someclass::dojson() {
using boost::property_tree::ptree;
ptree pt;
std::stringstream ss(json_data_string);
read_json(ss,pt);
}
Ahora json_data_string es diferente entre las dos clases (solo son datos json recibidos a través de un socket).
Entonces, ¿el hilo read_json es seguro o tengo que excluirlo (más bien no) o hay una mejor manera de llamar a read_json que es seguro para subprocesos?
Debido a que boost json parser depende de boost :: spirit, y spirit no es el valor predeterminado de seguridad de subprocesos.
Puede agregar esta macro antes de cualquier archivo de encabezado de ptree para resolverla.
#define BOOST_SPIRIT_THREADSAFE
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
Gracias a Wei y liquidblueocean; la # definición solucionó mi problema. Para tu información, estaba obteniendo este rastro de pila de windbg! Analyse -v cuando tenía dos subprocesos que llamaban 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 :: grammar, _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>>>>, boost :: spirit :: classic :: parser_context>> + 0x28 [c: / ss / tp / aoo341 / main / boost / rel / inc / boost / spirit / home / classic / core / non_terminal / impl / grammar.ipp @ 325] 1 2 07b3e54c 002224fa sseng! Boost :: spirit :: classic :: grammar, _STL :: allocator>, _ STL :: basic_string, _STL :: allocator>, _ STL :: less <_STL :: basic_string, _STL :: allocator>>> >, boost :: spirit :: classic :: parser_context> :: ~ grammar, _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 sseng! 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 / mai n / 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 :: Handshake + 0x15b [c: / ss / xl / src / cpp / bin / eng / ssphone.cpp @ 272] 17 07b3ea5c 00234fc7 sseng! SSPhone :: OnEvent + 0x1a9 [c: s sc] / xl / src / cpp / bin / eng / ssphone.cpp @ 232] 18 07b3fb7c 6f6b3433 sseng! PhoneThreadFunc + 0x1ed [c: / ss / xl / src / cpp / bin / eng / ssthrd.cpp @ 198]
JSON parser in ptree ha sido reescrito y lanzado en Boost 1.59. Agregar BOOST_SPIRIT_THREADSAFE
define ya no es necesario para Property Tree ya que ya no usa Boost.Spirit
y la función read_json
puede considerarse segura para subprocesos.
TL; DR:
Mi sugerencia: utilizar el lenguaje de intercambio atómico
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
}
Ahora, si necesita bloquear el árbol compartido antes de leer, depende de su conocimiento del contexto de subprocesos (en otras palabras, depende de si sabe que su árbol puede ser modificado al mismo tiempo).
Para hacer las cosas increíblemente flexibles, haga lo mismo a través de shared_ptr<ptree>
, pero esto shared_ptr<ptree>
una sobrecarga considerable. Lo anterior es que, con el idioma de intercambio, no tendrá que bloquear las cosas en el lado de lectura, ya que los lectores continuarán felizmente leyendo el viejo árbol, y si terminan de leer y liberan el shared_ptr
, se destruirá al final .
No estoy completamente seguro de lo que esperas. Con el árbol de propiedades al que se accede para escribir desde dos subprocesos nunca será seguro para subprocesos sin bloqueo. Por lo tanto, asumo que quiere decir que es un árbol de propiedades seguro para la lectura mientras se analiza simultáneamente en otro lugar.
Aquí, mi principal expectativa es: no. C ++ tiene una cultura de "pago por lo que necesita" que no verá ninguna clase de propósito general segura para subprocesos. Habría la opción de
- un preprocesador # define para activar la seguridad de subprocesos
- Un parámetro de plantilla de política que gobierna el comportamiento.
Después de mirar el código fuente, sorprendentemente, parece que era casi seguro para subprocesos. Pero no del todo :)
Parece que no hay #define o marca para configurar para que el subproceso del árbol de propiedades sea seguro, por lo que está bloqueado.
Razón fundamental:
Mirando a internal_read_json
veo que solo accede a la transmisión (que debería ser privada para este lector de todos modos, ya que compartir transmisiones entre múltiples usuarios (concurrentes) casi nunca es útil 1 ), y luego, muy correctamente, solo intercambia las de ptree ( pt
) nodo raíz hacia fuera con el árbol de contexto del analizador.
Obviamente, la funcionalidad de intercambio atómico está principalmente allí para la seguridad de excepciones (no desea cambiar su árbol de notas si se produce una excepción en la mitad del análisis del JSON). Sin embargo, si la operación de intercambio de IFF fuera segura para subprocesos, esto también haría que el acceso a pt
seguro.
Por desgracia, en la implementación de ptree, vemos que el intercambio no es seguro para subprocesos:
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);
}
Por un lado, podría tener una condición de carrera entre intercambiar m_data
y m_children
; además, los intercambios son estándar, no intercambios atómicos.
1 además de istringstream
obviamente no es seguro para subprocesos ya que es una clase de biblioteca estándar C ++ 98