2012-05-18 13 views
5

Sto tentando di condividere un'immagine, che viene utilizzata solo in sola lettura, attraverso i thread. Tipicamente faccio questo genere di cose con boost :: shared_ptrs ma dato che cv :: Mat è già un contenitore di conteggio di riferimento sotto, ho cercato di usarlo nello stesso modo assumendo che sia thread-safe basato sui riferimenti alla sicurezza dei thread in riferimento a contare qui:È cv :: Mat thread safe (assegnazione atomica + refcounting)?

Tuttavia sto avendo avendo problemi che potrebbero, eventualmente, indicano che essi sono infatti non thread-safe; quell'assegnazione è non atomica. Occasionalmente avrò un errore seg in un incremento del conteggio dei riferimenti che implica che l'oggetto originale è già stato distrutto.

Quindi la domanda specifica è:

  • È assegnazione cv :: Mat atomica?

risposta

5

Domanda specifica, risposta breve: SÌ.

È possibile controllare il cv :: Mat dettagli di implementazione in core/src/matrix.cpp e include/.../core/core.hpp

Alcuni estratti di codice da fonti OpenCV:

if(refcount) 
     CV_XADD(refcount, 1); 

Dove CV_XADD è l'atomica test-e-incremento.

inline void Mat::addref() 
{ if(refcount) CV_XADD(refcount, 1); } 

inline void Mat::release() 
{ 
    if(refcount && CV_XADD(refcount, -1) == 1) 
     deallocate(); 
    data = datastart = dataend = datalimit = 0; 
    size.p[0] = 0; 
    refcount = 0; 
} 

extra

puntatori intelligenti offrono un livello di sicurezza thread, ma questo non significa che sono completamente thread-safe in ogni scenario possibile. Nello specifico, se provi a copiare un ptr condiviso nello stesso momento in cui viene distrutto da un altro thread, perdi. Questo non è un bug nell'implementazione, ma un compromesso tra velocità e utilità.

Tutte le principali implementazioni di ptr condivise (boost, stl) seguono questo approccio.

+0

Questo non sembra essere atomico. 'If' potrebbe restituire true, potrebbe verificarsi un cambio di contesto, una release e destroy potrebbe accadere, e quindi il contesto tornerà indietro e CV_XADD sarebbe segfault, a meno che manchi qualcosa. – Catskul

+0

CV_XADD è un test-and-set atomico (quindi, verifica il conto prima di aggiungere). la prima parte (se refcount) è lì per un test veloce prima di entrare nello stato op atomico. – Sam

+0

Inoltre, questo codice, come so, viene rilasciato dai ragazzi di Google.Non penso che farebbero un simile errore. – Sam

4

No, l'assegnazione non è perfettamente thread-safe.

Ho scritto un programma di test che crea due thread. Entrambi contengono un shared_ptr per un oggetto che contiene un cv :: Mat. Un thread assegna quello cv :: Mat a un'immagine generata casualmente mentre l'altro thread crea una copia locale di quel cv :: Mat.

Questo si blocca immediatamente con un doppio libero. Se il thread di scrittura sovrascrive il precedente mentre il thread di copia inizia la copia, verrà copiato un cv :: Mat il cui dato interno ptr è stato appena cancellato. Quando la copia locale del thread di copia esce dall'ambito, tenta di liberarla di nuovo.

volatile bool g_done = false; 

struct Object 
{ 
    cv::Mat cvMask; 
}; 

//////////////////////////////////////////////////////////////////////// 
//////////////////////////////////////////////////////////////////////// 
void thread1(boost::shared_ptr<Object> sharedObj) 
{ 
    while(!g_done) 
    { 
     sharedObj->cvMask = cv::Mat::ones(1 + (rand()% 1024), 1+(rand()%768), CV_8UC1); 
    } 
} 

//////////////////////////////////////////////////////////////////////// 
//////////////////////////////////////////////////////////////////////// 
void thread2(boost::shared_ptr<Object> sharedObj) 
{ 
    while(!g_done) 
    { 
     cv::Mat localCopy = sharedObj->cvMask; 
    } 
} 

//////////////////////////////////////////////////////////////////////// 
//////////////////////////////////////////////////////////////////////// 
void sigHandler(int signum) 
{ 
    fprintf(stderr, "Quitting...\n"); 
    g_done = true; 
} 

//////////////////////////////////////////////////////////////////////// 
//////////////////////////////////////////////////////////////////////// 
int main(int argc, char** argv) 
{ 
    signal(SIGINT, sigHandler); 

    boost::shared_ptr<Object> sharedObj(new Object); 
    sharedObj->cvMask = cv::Mat::ones(1024,768, CV_8UC1); 

    boost::thread* t1 = new boost::thread(boost::bind(&thread1, _1), sharedObj); 
    boost::thread* t2 = new boost::thread(boost::bind(&thread2, _1), sharedObj); 

    while(!g_done) 
    { 
     usleep(1e6); 
    } 

    t1->join(); 
    t2->join(); 
    delete t1; 
    delete t2; 

    return 0; 
}