5

Sto creando un programma che riceverà i messaggi da una macchina remota e ha bisogno di scrivere i messaggi su un file su disco. La difficoltà che sto riscontrando risiede nel fatto che lo scopo di questo programma è quello di testare le prestazioni della libreria che riceve i messaggi e, pertanto, ho bisogno di assicurarmi che scrivere i messaggi sul disco non influenzi le prestazioni della libreria . La libreria consegna i messaggi al programma tramite una funzione di callback. Un'altra difficoltà è che la soluzione deve essere indipendente dalla piattaforma.Come implementare la scrittura asincrona indipendente da piattaforma su file?

Quali opzioni ho?

ho pensato di quanto segue:

  • utilizzando boost:asio a scrivere nel file, ma a quanto pare (vedi this documentazione) che scrittura asincrona di file si trova nella parte specifica di Windows di questa biblioteca - quindi questo non può essere Usato.
  • Utilizzare boost::interprocess per creare una coda di messaggi ma this documentation indica che ci sono 3 metodi in cui i messaggi possono essere inviati e tutti i metodi richiederebbero il blocco del programma (implicitamente o meno) se la coda dei messaggi è piena, cosa che non posso rischio.
  • creazione di un std::deque<MESSAGES> per passare alla deque dalla funzione di richiamata e far uscire i messaggi durante la scrittura sul file (su un thread separato), ma i contenitori STL sono not guaranteed to be thread-safe. Potrei bloccare la spinta e aprire il deque, ma stiamo parlando di 47 microsecondi tra i messaggi successivi, quindi vorrei evitare del tutto le serrature.

Qualcuno ha più idee su possibili soluzioni?

+0

47micros tra quali messaggi? È la cadenza della macchina remota? –

risposta

2

I contenitori STL potrebbero non essere thread-safe ma non ne ho mai toccato uno che non può essere utilizzato in momenti diversi su thread diversi. Passare la proprietà a un altro thread sembra sicuro.

ho usato il seguente un paio di volte, quindi so che funziona:

  • creare un puntatore ad uno std :: vector.
  • Creare un blocco mutex per proteggere il puntatore del vettore.
  • Usa nuovo [] per creare uno std :: vector e quindi reserve() di grandi dimensioni.

Nel thread ricevitore:

  • bloccare il mutex ogni volta aggiungendo un elemento alla coda. Questo dovrebbe essere un blocco corto.
  • Aggiungi articolo in coda.
  • Rilasciare il blocco.
  • Se si sente che segnala una variabile di condizione. A volte no: dipende dal design. Se il volume è molto alto e non vi è alcuna pausa nel lato di ricezione, basta saltare la condizione e sondare invece.

Sul filo dei consumatori (l'autore del disco):

  • andare a cercare lavoro da fare per il polling o in attesa su una variabile di condizione:
  • Blocca il mutex coda.
  • Controllare la lunghezza della coda.
  • Se c'è lavoro nella coda, assegnare il puntatore a una variabile nel thread consumatore.
  • Utilizzare new [] e reserve() per creare un nuovo vettore di coda e assegnarlo al puntatore della coda.
  • Sblocca il mutex.
  • Spegni e scrivi i tuoi oggetti su disco.
  • elimina [] il vettore della coda utilizzata.

Ora, a seconda del problema, potrebbe essere necessario un modo per bloccare. Ad esempio, in uno dei miei programmi, se la lunghezza della coda raggiunge mai 100.000 elementi, il thread di produzione inizia appena a dormire per 1 secondo e si lamenta molto. È una di quelle cose che non dovrebbe accadere, eppure lo è, quindi dovresti prenderla in considerazione. Senza alcun limite, userà tutta la memoria sulla macchina e poi si bloccherà con un'eccezione, verrà ucciso da OOM o si fermerà in una tempesta di swap.

+0

Ho appena letto l'altra risposta. È possibile adattare anche questo requisito se il blocco per articolo è troppo costoso. Basta tenere un vettore di puntatori vettoriali e aggiungere un vettore solo quando hai finito di scriverlo. –

+0

Un'altra nota: questo design ha funzionato molto bene su Linux dove i mutex utilizzano futex che hanno un costo molto basso quando non ci sono contese. E non è male su BSD o Windows utilizzando CriticalSection. Ma dal momento che si desidera essere molto multipiattaforma, il blocco del contenitore nidificato potrebbe essere davvero il migliore. –

2

boost :: thread è indipendente dalla piattaforma, quindi dovresti essere in grado di utilizzarlo per creare una discussione su cui eseguire le scritture di blocco. Per evitare la necessità di bloccare il contenitore ogni volta che un messaggio viene inserito nella filettatura principale è possibile utilizzare una modifica sulla tecnica doppio buffer creando contenitori inseriti, quali:

std::deque<std::deque<MESSAGES> > 

quindi bloccare solo deque livello superiore quando un deque pieno di messaggi è pronto per essere aggiunto. A sua volta, il thread di scrittura bloccherebbe solo la deque di livello superiore per far scattare una deque piena di messaggi da scrivere.

Problemi correlati