2012-05-18 7 views
5

Ich versuche, ein Bild zu teilen, das nur schreibgeschützt über Threads verwendet wird. Normalerweise mache ich diese Art von Sache mit boost :: shared_ptrs, aber da cv :: Mat bereits ein Referenzzähler Container darunter ist, habe ich versucht, es auf die gleiche Weise zu verwenden, vorausgesetzt, dass es threadsicher basierend auf Verweisen auf Thread-Sicherheit ist in Bezug zu zählen hier:Ist cv :: Mat Thread-sicher (atomare Zuweisung + Refcounting)?

aber ich habe mit worden Probleme hat, die möglicherweise hinweisen könnten, dass sie infact nicht Thread sicher sind; Diese Zuordnung ist nicht atomar. Gelegentlich erhalte ich einen Seg-Fehler innerhalb eines Referenzzählinkrements, der impliziert, dass das ursprüngliche Objekt bereits zerstört wurde.

So ist die konkrete Frage lautet:

  • Ist cv :: Mat Zuordnung Atom?

Antwort

5

Spezifische Frage, kurze Antwort: JA.

Sie können die cv überprüfen :: Mat Implementierungsdetails in core/src/matrix.cpp und include/.../core/core.hpp

einige Code-Ausschnitte aus OpenCV Quellen:

if(refcount) 
     CV_XADD(refcount, 1); 

Wo CV_XADD den Atomtest-and-Zuwachs ist.

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-

Intelligente Zeiger tun ein Niveau der Thread-Sicherheit bieten, aber das bedeutet nicht, dass sie in jedem Szenario möglich vollständig Thread-sicher sind. Wenn Sie versuchen, ein freigegebenes PTR gleichzeitig zu kopieren, wenn es von einem anderen Thread zerstört wird, verlieren Sie. Das ist kein Fehler in der Implementierung, sondern ein Design-Trade-Off zwischen Geschwindigkeit und Nützlichkeit.

Alle wichtigen gemeinsamen Implementierungen von ptr (boost, stl) folgen diesem Ansatz.

+0

Das scheint nicht atomar zu sein. Das 'if' könnte true zurückgeben, ein Kontextwechsel könnte passieren, ein Release und eine Zerstörung könnten eintreten, und dann würde der Kontext zurückschalten und CV_XADD würde segfault werden, außer wenn ich etwas verpasse. – Catskul

+0

CV_XADD ist ein atomarer Test-and-Set (so testet es vor dem Hinzufügen auf refcount). der erste Teil (wenn Refcount) ist für einen schnellen Test vor dem Eintritt in atomare Op-Zustand. – Sam

+0

Außerdem wird dieser Code, wie ich weiß, von den Jungs bei Google veröffentlicht.Ich glaube nicht, dass sie einen solchen Fehler machen würden. – Sam

4

Nein, die Zuordnung ist nicht absolut threadsicher.

Ich schrieb ein Testprogramm, das zwei Threads erstellt. Beide enthalten ein shared_ptr zu einem Objekt, das eine cv :: Mat enthält. Ein Thread weist cv :: Mat einem zufällig erzeugten Bild zu, während der andere Thread eine lokale Kopie des cv :: Mat erstellt.

Dies stürzt sofort mit einem Doppel-frei. Wenn der Schreib-Thread den vorherigen überschreibt, während der Kopier-Thread zu kopieren beginnt, kopiert er einen cv :: Mat, dessen interne Daten ptr gerade gelöscht wurde. Wenn die lokale Kopie des Kopier-Threads den Gültigkeitsbereich verlässt, wird versucht, sie wieder freizugeben.

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