Opencv cv::Mat线程安全吗(原子赋值和引用计数)?
我正在尝试跨线程共享一个仅以只读方式使用的映像。通常,我使用boost::shared_ptrs执行这类操作,但由于cv::Mat下面已经是一个引用计数容器,因此我一直尝试以相同的方式使用它,假设它是基于引用计数中对线程安全的引用的线程安全的:Opencv cv::Mat线程安全吗(原子赋值和引用计数)?,opencv,thread-safety,atomic,reference-counting,Opencv,Thread Safety,Atomic,Reference Counting,我正在尝试跨线程共享一个仅以只读方式使用的映像。通常,我使用boost::shared_ptrs执行这类操作,但由于cv::Mat下面已经是一个引用计数容器,因此我一直尝试以相同的方式使用它,假设它是基于引用计数中对线程安全的引用的线程安全的: 然而,我一直有一些问题,可能表明它们实际上不是线程安全的;这个任务是非原子的。偶尔,我会在引用计数增量中得到一个seg错误,这意味着原始对象已经被破坏 因此,具体问题是: cv::Mat赋值是原子的吗 具体问题,简短回答:是 您可以在core/
- cv::Mat赋值是原子的吗
- 具体问题,简短回答:是
您可以在
core/src/matrix.cpp
和include/../core/core.hpp
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)都遵循这种方法。不,分配不是完全线程安全的 我编写了一个创建两个线程的测试程序。它们都包含对包含cv::Mat的对象的共享ptr。一个线程将该cv::Mat分配给随机生成的图像,而另一个线程生成该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;
}
volatile bool g_done=false;
结构对象
{
cv::Mat-cvMask;
};
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
void thread1(boost::shared_ptr sharedObj)
{
而(!g_done)
{
sharedObj->cvMask=cv::Mat::ones(1+(rand()%1024)、1+(rand()%768)、cv_8UC1);
}
}
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
void thread2(boost::shared_ptr sharedObj)
{
而(!g_done)
{
cv::Mat localCopy=sharedObj->cvMask;
}
}
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
无效信号器(内部信号)
{
fprintf(标准“退出…\n”);
g_done=真;
}
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
int main(int argc,字符**argv)
{
信号(SIGINT、sigHandler);
boost::shared_ptr sharedObj(新对象);
sharedObj->cvMask=cv::Mat::one(1024768,cv_8UC1);
boost::thread*t1=新的boost::thread(boost::bind(&thread1,_1),sharedObj);
boost::thread*t2=新的boost::thread(boost::bind(&thread2,_1),sharedObj);
而(!g_done)
{
usleep(1e6);
}
t1->join();
t2->join();
删除t1;
删除t2;
返回0;
}
这似乎不是原子的。如果
可以返回true,则可能会发生上下文切换、释放和销毁,然后上下文切换返回,CV_XADD将segfault,除非我遗漏了什么。CV_XADD是一个原子测试和设置(因此,它在添加之前测试refcount)。第一部分(如果refcount)是在进入原子操作状态之前进行快速测试。此外,正如我所知,这段代码是由Google的人员发布的。我不认为他们会犯这样的错误。经过一点调查后,CV_XADD似乎没有检查refcount ptr的值,但无论如何,deallocate
和refcount=0
之间的代码似乎没有受到保护,因此可能同时存在上下文切换和segfault。(AFAIK OpenCV在从Intel移交给Willow Garage后由Willow Garage维护)您实际上错过了一些东西。refcount为零有一个特殊的含义:指向的数据是外部的,OpenCV永远不应该删除这些数据。因此,除此之外,refcount将始终是一个有效指针,并且只有当它达到零时才会被删除。但是你可以确定没有其他悬而未决的指针了。