Opencv 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/

我正在尝试跨线程共享一个仅以只读方式使用的映像。通常,我使用boost::shared_ptrs执行这类操作,但由于cv::Mat下面已经是一个引用计数容器,因此我一直尝试以相同的方式使用它,假设它是基于引用计数中对线程安全的引用的线程安全的:

然而,我一直有一些问题,可能表明它们实际上不是线程安全的;这个任务是非原子的。偶尔,我会在引用计数增量中得到一个seg错误,这意味着原始对象已经被破坏

因此,具体问题是:

  • 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将始终是一个有效指针,并且只有当它达到零时才会被删除。但是你可以确定没有其他悬而未决的指针了。