2012-05-18 7 views
5

Estoy intentando compartir una imagen, que solo se usa solo lectura, entre subprocesos. Normalmente hago este tipo de cosas con boost :: shared_ptrs, pero como cv :: Mat ya es un contenedor de conteo de referencia debajo, he intentado usarlo de la misma manera suponiendo que es seguro para subprocesos basado en las referencias a seguridad de subprocesos en referencia a contar aquí:Es cv :: Mat thread-safe (asignación atómica + refcounting)?

sin embargo he estado teniendo problemas con que podrían posiblemente indican que están de hecho no seguro para subprocesos; esa asignación no es atómica. Ocasionalmente obtendré un seg-fault dentro de un incremento de recuento de referencia que implica que el objeto original ya ha sido destruido.

Así que la pregunta concreta es:

  • es la asignación cv :: Mat atómica?

Respuesta

5

Pregunta específica, respuesta breve: SÍ.

puede comprobar el cv :: Mat detalles de implementación en core/src/matrix.cpp y include/.../core/core.hpp

Algunos extractos de código de fuentes OpenCV:

if(refcount) 
     CV_XADD(refcount, 1); 

Dónde CV_XADD es la prueba-y-incremento atómica.

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

punteros inteligentes ofrecen un nivel de seguridad de los subprocesos, pero eso no quiere decir que son completamente seguros para subprocesos en todos los escenarios posibles. Específicamente, si intentas copiar un ptr compartido al mismo tiempo que es destruido por otro hilo, pierdes. Eso no es un error en la implementación, sino un compromiso de diseño entre velocidad y utilidad.

Todas las principales implementaciones de ptr compartidas (boost, stl) siguen este enfoque.

+0

Eso no parece ser atómico. El 'si' podría volverse verdadero, podría ocurrir un cambio de contexto, podría ocurrir una liberación y destruir, y luego el contexto retrocedería y CV_XADD segmentaría la falla, a menos que me faltara algo. – Catskul

+0

CV_XADD es un conjunto de prueba atómica (por lo tanto, prueba el recuento antes de agregarlo). la primera parte (si es refcount) está ahí para una prueba rápida antes de ingresar al estado op atómico. – Sam

+0

Además, este código, como sé, es lanzado por los chicos de Google.No creo que cometan un error así. – Sam

4

No, la asignación no es perfectamente segura para hilos.

Escribí un programa de prueba que crea dos hilos. Ambos contienen un shared_ptr a un objeto que contiene un cv :: Mat. Un hilo asigna ese cv :: Mat a una imagen generada al azar, mientras que el otro hilo hace una copia local de ese cv :: Mat.

Esto se bloquea inmediatamente con una doble libre. Si el hilo de escritura sobrescribe el anterior ya que el hilo de copiado comienza a copiarse, copiará un cv :: Mat que está acabando de borrar el archivo de datos internos. Cuando la copia local del hilo de copiado queda fuera del alcance, intenta liberarla de nuevo.

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