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

时间:2012-05-18 18:24:22

标签: opencv thread-safety atomic reference-counting

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

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

所以具体问题是:

  • cv :: Mat赋值原子?

2 个答案:

答案 0 :(得分:5)

具体问题,简答:是的。

您可以在core/src/matrix.cppinclude/.../core/core.hpp

中查看cv :: Mat实施详情

OpenCV来源的一些代码摘录:

 if( refcount )
        CV_XADD(refcount, 1);

其中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;
}

<强>附加

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

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

答案 1 :(得分:5)

不,分配不是完全线程安全的。

我写了一个创建两个线程的测试程序。它们都包含一个包含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;
}