我正在尝试跨线程共享仅以只读方式使用的图像。通常,我会使用boost :: shared_ptrs进行此类操作,但是由于cv :: Mat已经是其下的引用计数容器,因此我一直尝试以相同的方式使用它,前提是基于对线程安全的引用,假定它是线程安全的参考引用在这里:
但是我遇到的问题可能表明它们实际上是不安全的;该分配是非原子的。有时,我会在参考计数增量内出现段错误,这表明原始对象已被破坏。
所以具体问题是:
不,该分配不是完全线程安全的。
我编写了一个创建两个线程的测试程序。它们都包含一个包含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;
}
特定问题,简短答案:是。
您可以在core/src/matrix.cpp
和include/.../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)都遵循这种方法。