2009-04-23 5 views
6

Ho bisogno di condividere una pila di stringhe tra i processi (possibilmente oggetti più complessi in futuro). Ho deciso di usare boost :: interprocess ma non riesco a farlo funzionare. Sono sicuro che è perché non capisco qualcosa. Ho seguito il loro esempio, ma sarei davvero grato se qualcuno con esperienza nell'utilizzo di quella libreria possa dare un'occhiata al mio codice e dirmi cosa c'è che non va. Il problema è che sembra funzionare ma dopo alcune iterazioni ottengo tutti i tipi di eccezioni sia sul processo del lettore che a volte sul processo di scrittura. Ecco una versione semplificata della mia realizzazione:Boost, memoria condivisa e vettori

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; 
}; 

EDIT A cura di utilizzare named_mutex. Il codice originale utilizzava interprocess_mutex che non è corretto, ma non era questo il problema.

EDIT2 Vorrei anche notare che le cose funzionano fino a un certo punto. Il processo di scrittura può spingere diverse stringhe (o una stringa molto grande) prima che il lettore si rompa. Il lettore si spezza in un modo che la riga m_stack-> begin() non fa riferimento a una stringa valida. È spazzatura. E poi l'ulteriore esecuzione genera un'eccezione.

EDIT3 Ho modificato la classe per utilizzare boost :: interprocess :: string piuttosto che std :: string. Ancora il lettore ha esito negativo con indirizzo di memoria non valido. Ecco il lettore/scrittore

//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

Sono terribilmente dispiaciuto per i poster precedenti. Ho cliccato "cancella" per errore e cancellato il mio post originale di questa stessa identica domanda. – Budric

+1

Non puoi annullare l'operazione? O funziona solo per le risposte? –

+0

Mi è permesso di imbavagliare le dimensioni dei tuoi metodi in linea? O questo mi segna come un noob in C++? –

risposta

4

Ci sono diverse cose che mi hanno fatto venire in mente la tua implementazione. Uno era l'uso di un puntatore all'oggetto mutex denominato, mentre la documentazione della maggior parte delle librerie di boost tende a piegarsi all'indietro per non utilizzare un puntatore. Questo mi porta a chiedere un riferimento al frammento di programma da cui hai lavorato per costruire il tuo caso di test, poiché ho avuto simili disavventure e talvolta l'unica via d'uscita era tornare all'esempio e andare avanti un passo alla volta fino a quando Mi imbatto nel cambiamento decisivo.

L'altra cosa che sembra discutibile è l'allocazione di un blocco da 65k per la memoria condivisa, e quindi nel codice di test, che va in loop a 1000000000, spingendo una stringa nello stack ogni iterazione.

Con un PC moderno in grado di eseguire 1000 istruzioni per microsecondo e oltre, e sistemi operativi come Windows che eliminano ancora quantità di esecuzione in 15 millisecondi. Chunk, non ci vorrà molto tempo per traboccare quella pila. Questa sarebbe la mia prima ipotesi sul perché le cose siano in tilt.

P.S. Sono appena tornato dal fissare il mio nome a qualcosa che assomiglia alla mia vera identità. Poi l'ironia ha colpito il fatto che la mia risposta alla tua domanda ci ha guardato entrambi in faccia dall'angolo in alto a sinistra della pagina del browser! (Ovviamente, presumo che abbia avuto ragione, cosa che spesso non accade in questo caso.)

+0

Ho abbandonato la memoria condivisa e uso Poco :: SharedMemory che ha funzionato senza questo sforzo. Il codice di test non ha mai raggiunto molte iterazioni.Anche il lettore dovrebbe essere fuori dallo stack liberando la memoria. – Budric

+0

Questa è la parte che non ho potuto seguire - Non vedo alcun luogo o metodo specifico per un thread per rendere il controllo a un altro - quindi ho pensato che stavi lasciando scadere il tempo su uno, e poi l'altro, round-robin. Poiché Windows emette enormi quantità di tempo, riempirebbe il buffer molto prima che fossero trascorsi 15 millisecondi. Anche gli IOStreams in C++ sono spesso bufferizzati, quindi solo la presenza o l'estensione dell'output può essere fuorviante. Il modo per verificare la mia teoria è mettere una "zona di guardia" su ciascun lato del buffer e cercare la sovrascrittura. - ma hai fatto la scelta giusta per cambiare piano comunque. –

-3

Beh, forse la memoria condivisa non è il progetto giusto per il tuo problema per cominciare. Tuttavia non lo sapremmo, perché non sappiamo cosa si cerca di ottenere in primo luogo.