cv :: Mat线程安全(原子分配+引用计数)吗?

问题描述 投票:10回答:2

我正在尝试跨线程共享仅以只读方式使用的图像。通常,我会使用boost :: shared_ptrs进行此类操作,但是由于cv :: Mat已经是其下的引用计数容器,因此我一直尝试以相同的方式使用它,前提是基于对线程安全的引用,假定它是线程安全的参考引用在这里:

但是我遇到的问题可能表明它们实际上是不安全的;该分配是非原子的。有时,我会在参考计数增量内出现段错误,这表明原始对象已被破坏。

所以具体问题是:

  • cv :: Mat分配原子吗?
opencv thread-safety atomic reference-counting
2个回答
6
投票

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

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

此立即崩溃,并带有double-free。如果在复制线程开始复制时写入线程覆盖了先前的线程,它将复制内部数据ptr刚刚被删除的cv :: Mat。当复制线程的本地副本超出范围时,它将尝试再次释放它。

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

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)都遵循这种方法。

© www.soinside.com 2019 - 2024. All rights reserved.