2012-03-16 3 views
31

Circular_buffer dalla libreria boost non è thread-safe. Quindi ho spostato l'oggetto boost :: circular_buffer in una classe come mostrato di seguito. L'esclusione reciproca tra i thread viene raggiunta (credo) utilizzando variabili condizionali, un mutex e un'acquisizione/rilascio di blocco. Questo thread di implementazione è sicuro?Implementazione sicura del buffer circolare

#include <boost/thread/condition.hpp> 
#include <boost/thread/mutex.hpp> 
#include <boost/thread/thread.hpp> 
#include <boost/circular_buffer.hpp> 

// Thread safe circular buffer 
template <typename T> 
class circ_buffer : private boost::noncopyable 
{ 
public: 
    typedef boost::mutex::scoped_lock lock; 
    circ_buffer() {} 
    circ_buffer(int n) {cb.set_capacity(n);} 
    void send (T imdata) { 
     lock lk(monitor); 
     cb.push_back(imdata); 
     buffer_not_empty.notify_one(); 
    } 
    T receive() { 
     lock lk(monitor); 
     while (cb.empty()) 
      buffer_not_empty.wait(lk); 
     T imdata = cb.front(); 
     cb.pop_front(); 
     return imdata; 
    } 
    void clear() { 
     lock lk(monitor); 
     cb.clear(); 
    } 
    int size() { 
     lock lk(monitor); 
     return cb.size(); 
    } 
    void set_capacity(int capacity) { 
     lock lk(monitor); 
     cb.set_capacity(capacity); 
    } 
private: 
    boost::condition buffer_not_empty; 
    boost::mutex monitor; 
    boost::circular_buffer<T> cb; 
}; 

Modifica Questa è ora una classe template, che accetta un oggetto di qualsiasi tipo (non solo cv::Mat oggetto).

+4

Sembra che si adatterebbe meglio su [CodeReview] (http://codereview.stackexchange.com/). –

+1

Perdona la mia domanda stupida, ma dove è necessario un buffer circolare sicuro per thread? In tutti i punti in cui ho mai usato un buffer circolare, sarebbe stato un brutto errore accedervi da più thread come questo. Quindi, solo per curiosità, qual è il tuo caso d'uso per questo? – LiKao

+1

@LiKao Lo uso per afferrare fotogrammi dalle telecamere di rete in MATLAB, vedere il mio post precedente http://stackoverflow.com/questions/9472880/how-to-implement-a-circular-buffer-of-cvmat-objects-opencv . Come ti avvicineresti a questo? – Alexey

risposta

16

Sì.
Se si bloccano tutti i metodi pubblici con lo stesso blocco, sarà protetto da thread.

Si potrebbe considerare l'utilizzo di read-write locks, che può avere prestazioni migliori se si dispone di molti lettori concorrenti.

Se non si dispone di molti lettori, aggiungerà solo un sovraccarico, ma potrebbe essere opportuno verificare l'opzione e il test.

+10

Non penso che i blocchi di lettura/scrittura abbiano senso in un buffer circolare. Sia i produttori che i consumatori modificano il buffer, quindi tutti sono in realtà * scrittori *. –

+0

@ DavidRodríguez-dribeas - Hai ragione in questo caso.Non sono entrato nel design, solo la parte di sicurezza del filo. –

0

Sembra buono a prima vista, tranne che non si sta utilizzando la condizione buffer_not_full. Probabilmente vorrai aggiungere un codice simile al codice buffer_not_empty.

+1

Se un'origine dati produce più dati di quanti possano essere contenuti nel buffer, l'oggetto boost :: circular_buffer scrive sopra i dati più vecchi. Questo va bene. Quindi la condizione 'buffer_not_full' non ha bisogno di essere controllata. – Alexey

5

penso che sia a posto, tranne che ci sono alcune inutili copie di Mat realizzate in send. Non hai bisogno del nuovo, puoi direttamente spingere l'argomento di send al tuo cb.

+0

+1 per evitare copie inutili, costose e con aumento della contesa. –

+0

@MartinJames Non riesco a spingere direttamente l'argomento di invio. "cv :: La classe Mat implementa il conteggio dei riferimenti e la copia superficiale in modo tale che quando un'immagine viene assegnata a un altro, i dati dell'immagine non vengono copiati e entrambe le immagini puntano allo stesso blocco di memoria." "Viene mantenuto un conteggio di riferimento tale che la memoria verrà rilasciata solo quando tutti i riferimenti all'immagine verranno distrutti Se si desidera creare un'immagine che conterrà una nuova copia dell'immagine originale, si utilizzerà il metodo copyTo() ". - da "Ricettario di programmazione applicativa OpenCV 2 Computer Vision" (pagina 28). – Alexey

+1

non hai ancora bisogno del nuovo in questo caso, l'allocazione della nuova immagine nello stack funziona altrettanto bene. Ma non è questa una caratteristica che vuoi, la copia condivisa nel tuo buffer circolare? –

2

L'implementazione è simile a quella mostrata da questo blogger. Dovresti leggere quel blog per vedere se hai perso qualcosa nella tua implementazione.

Se gli oggetti Mat sono costosi da creare/copiare, evitare di creare/copiare/eliminare continuamente. Invece, dovresti avere un pool (o lista libera) di oggetti Mat che ottengono continuamente riciclato in una sorta di architettura della pipeline. Descrivo questo tipo di architettura in questo answer in una domanda correlata.

In questa risposta, ho suggerito di utilizzare uno stack di blocco per implementare il pool, ma è anche possibile utilizzare il blocco circular_buffer. Il motivo per cui ho suggerito una pila è perché pensavo che potesse essere più facile da usare nella cache, ma non ho mai effettivamente misurato per vedere se avrebbe fatto la differenza.

+0

Gli oggetti mat (immagini) non sono troppo grandi e hanno praticamente le stesse dimensioni durante ogni chiamata. Ho appena realizzato che posso allocare oggetti Mat nello stack. – Alexey

+0

Utilizzo pool di oggetti (basati su code di blocco) da molto tempo. Infatti, utilizzo lo schema poolObject + message-passing quasi esclusivamente per i miei progetti di app multithread. Il no-copy, no-malloc, no-GC e il controllo del flusso integrato non sono gli unici vantaggi. Nelle app GUI, la creazione di pool di oggetti prima di qualsiasi form o thread di lavoro significa che di solito posso dimenticare la distruzione del pool esplicita o la seccatura del thread-termination. Il dumping dei livelli del pool su un timer mostra perdite senza miserabili strumenti di perdita di terze parti come V ******* che rallentano l'app di wole in una scansione. –

+0

Per impostazione predefinita, il costruttore di copie di cv :: Mat crea una copia superficiale. – Reunanen