是cv :: Mat线程安全(原子赋值+引用计数)?

Cat*_*kul 9 opencv atomic reference-counting thread-safety

我试图在线程之间共享一个只用于只读的映像.通常我使用boost :: shared_ptrs做这种事情,但由于cv :: Mat已经是下面的引用计数容器,我一直试图以相同的方式使用它,假设它是基于对线程安全的引用的线程安全在这里引用计数:

但是我遇到的问题可能表明它们实际上并不是线程安全的; 该任务是非原子的.偶尔我会在引用计数增量内得到一个seg-fault,这意味着原始对象已被破坏.

所以具体问题是:

  • cv :: Mat赋值原子?

小智 6

不,赋值不是完全线程安全的.

我写了一个创建两个线程的测试程序.它们都包含一个包含cv :: Mat的对象的shared_ptr.一个线程将cv :: Mat分配给随机生成的图像,而另一个线程生成该cv :: Mat的本地副本.

这个双重免费立即崩溃.如果写入线程在复制线程开始复制时覆盖前一个,它将复制一个cv :: Mat,其内部数据ptr刚被删除.当复制线程的本地副本超出范围时,它会尝试再次释放它.

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;
}
Run Code Online (Sandbox Code Playgroud)


Sam*_*Sam 5

具体问题,简答:是的.

您可以在core/src/matrix.cpp和中查看cv :: Mat实现细节include/.../core/core.hpp

一些代码摘自OpenCV来源:

 if( refcount )
        CV_XADD(refcount, 1);
Run Code Online (Sandbox Code Playgroud)

其中CV_XADD是原子测试和增量.

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;
}
Run Code Online (Sandbox Code Playgroud)

额外

智能指针确实提供了一定程度的线程安全性,但这并不意味着它们在每种情况下都是完全线程安全的.具体来说,如果您尝试复制共享ptr,同时它被另一个线程破坏,则会丢失.这不是实现中的错误,而是速度和实用性之间的设计权衡.

所有主要的共享ptr实现(boost,stl)都遵循这种方法.

  • 您实际上缺少了一些东西。refcount为零有特殊含义:指向的数据是外部的,切勿由OpenCV删除。因此,除了这种情况之外,refcount将始终是有效的指针,并且仅在达到零时才被删除。但是,您可以确定没有其他悬空指针了。 (2认同)