2009-04-23 14 views
6

Necesito compartir una pila de cadenas entre procesos (posiblemente objetos más complejos en el futuro). Decidí usar boost :: interprocess pero no puedo hacer que funcione. Estoy seguro de que es porque no estoy entendiendo algo. Seguí su ejemplo, pero realmente agradecería que alguien con experiencia en el uso de esa biblioteca pueda echar un vistazo a mi código y decirme qué sucede. El problema es que parece funcionar, pero después de algunas iteraciones recibo todo tipo de excepciones tanto en el proceso de lectura como en el proceso de escritura. Aquí hay una versión simplificada de mi aplicación:Boost, memoria compartida y vectores

using namespace boost::interprocess; 
class SharedMemoryWrapper 
{ 
public: 
    SharedMemoryWrapper(const std::string & name, bool server) : 
     m_name(name), 
     m_server(server) 
    { 
     if (server) 
     { 
      named_mutex::remove("named_mutex"); 
      shared_memory_object::remove(m_name.c_str()); 
      m_segment = new managed_shared_memory (create_only,name.c_str(),65536);   
      m_stackAllocator = new StringStackAllocator(m_segment->get_segment_manager()); 
      m_stack = m_segment->construct<StringStack>("MyStack")(*m_stackAllocator); 
     } 
     else 
     { 
      m_segment = new managed_shared_memory(open_only ,name.c_str()); 
      m_stack = m_segment->find<StringStack>("MyStack").first; 
     } 
     m_mutex = new named_mutex(open_or_create, "named_mutex"); 
    } 

    ~SharedMemoryWrapper() 
    { 
     if (m_server) 
     { 
      named_mutex::remove("named_mutex"); 
      m_segment->destroy<StringStack>("MyStack"); 
      delete m_stackAllocator; 
      shared_memory_object::remove(m_name.c_str()); 
     } 
     delete m_mutex; 
     delete m_segment; 
    } 

    void push(const std::string & in) 
    { 
     scoped_lock<named_mutex> lock(*m_mutex); 
     boost::interprocess::string inStr(in.c_str()); 
     m_stack->push_back(inStr); 
    } 
    std::string pop() 
    { 
     scoped_lock<named_mutex> lock(*m_mutex); 
     std::string result = ""; 
     if (m_stack->size() > 0) 
     { 
      result = std::string(m_stack->begin()->c_str()); 
      m_stack->erase(m_stack->begin()); 
     } 
     return result; 
    } 
private: 
    typedef boost::interprocess::allocator<boost::interprocess::string, boost::interprocess::managed_shared_memory::segment_manager> StringStackAllocator; 
    typedef boost::interprocess::vector<boost::interprocess::string, StringStackAllocator> StringStack; 
    bool m_server; 
    std::string m_name; 
    boost::interprocess::managed_shared_memory * m_segment; 
    StringStackAllocator * m_stackAllocator; 
    StringStack * m_stack; 
    boost::interprocess::named_mutex * m_mutex; 
}; 

EDITAR editado para usar named_mutex. El código original estaba usando interprocess_mutex que es incorrecto, pero ese no era el problema.

EDIT2 Debo señalar también que las cosas funcionan hasta cierto punto. El proceso de escritura puede empujar varias cadenas pequeñas (o una cadena muy grande) antes de que el lector se rompa. El lector se rompe de forma que la línea m_stack-> begin() no se refiere a una cadena válida. Es basura. Y luego la ejecución adicional arroja una excepción.

EDIT3 He modificado la clase para usar boost :: interprocess :: string en lugar de std :: string. Todavía el lector falla con una dirección de memoria no válida. Aquí está el lector/escritor

//reader process 
SharedMemoryWrapper mem("MyMemory", true); 
std::string myString; 
int x = 5; 
do 
{ 
    myString = mem.pop(); 
    if (myString != "") 
    { 
     std::cout << myString << std::endl; 
    } 
} while (1); //while (myString != ""); 

//writer 
SharedMemoryWrapper mem("MyMemory", false); 
for (int i = 0; i < 1000000000; i++) 
{ 
    std::stringstream ss; 
    ss << i; //causes failure after few thousand iterations 
    //ss << "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" << i; //causes immediate failure 
    mem.push(ss.str()); 
} 
return 0; 
+0

Lo siento muchísimo por los carteles anteriores. Hice clic en "eliminar" por error y borré mi publicación original de esta misma pregunta. – Budric

+1

¿No puedes recuperar? ¿O eso solo funciona para las respuestas? –

+0

¿Puedo mordazar el tamaño de sus métodos en línea? ¿O eso me marca como un novato en C++? –

Respuesta

4

Hay muchas cosas que me han llamado la atención acerca de su implementación. Uno fue el uso de un puntero al objeto mutex nombrado, mientras que la documentación de la mayoría de las bibliotecas impulsoras tiende a inclinarse hacia atrás para no usar un puntero. Esto me lleva a pedir una referencia al fragmento del programa en el que trabajó al crear su propio caso de prueba, ya que tuve desventuras similares y, a veces, la única salida fue volver al ejemplo y avanzar un paso a la vez hasta Me encuentro con el cambio de ruptura.

La otra cosa que parece cuestionable es su asignación de un bloque de 65k para memoria compartida, y luego en su código de prueba, haciendo un bucle a 1000000000, insertando una cadena en su pila cada iteración.

Con una PC moderna capaz de ejecutar 1000 instrucciones por microsegundo y más, y los sistemas operativos como Windows todavía reparten los plazos de ejecución en 15 milisegundos. trozos, no tardará en desbordar esa pila. Esa sería mi primera suposición de por qué las cosas están descontroladas.

P.S. Acabo de regresar de arreglar mi nombre a algo que se parece a mi identidad real. Luego, la ironía de que mi respuesta a tu pregunta nos haya estado mirando a los dos desde la esquina superior izquierda de la página del navegador. (Eso es, por supuesto, presumiendo que estaba en lo cierto, que a menudo no es el caso en este negocio).

+0

He abandonado impulsar la memoria compartida y uso Poco :: SharedMemory en su lugar, que funcionó sin tanto esfuerzo. El código de prueba nunca llegó a tantas iteraciones.Además, el lector debería sacar las cosas de la pila para liberar la memoria. – Budric

+0

Esa es la parte que no podía seguir, no veo ningún lugar o método específico para que un hilo ceda el control a otro, así que asumí que dejabas que el tiempo se agotara en uno, y luego en el otro, round-robin. Dado que Windows da grandes cantidades de tiempo, llenaría el búfer mucho antes de que transcurrieran 15 milisegundos. IOStreams en C++ también se amortiguan con frecuencia, por lo que solo la presencia o el alcance del resultado puede ser engañoso. La forma de verificar mi teoría es poner una "zona de guardia" a cada lado del búfer y buscar sobreescribir. - pero tomaste la decisión correcta de cambiar los planes de todos modos. –

-3

Bueno, tal vez la memoria compartida no es el diseño correcto para su problema. Sin embargo, no lo sabríamos, porque no sabemos lo que intentas lograr en primer lugar.

Cuestiones relacionadas