2017-02-05 143 views
1

我有一个纹理加载线程,它接收通过并发队列从主线程加载纹理的请求。C++多线程纹理加载器工作项从主线程中删除

纹理加载器请求是一个简单的结构与原料指向对象将接收质地:

struct TextureLoaderRequest 
{ 
    std::string mFilename; 
    ContentViewer *mContentViewer; 
}; 

包含在ContentViewer内的实际纹理对象由互斥锁和一些原子布尔保护(也包含在ContentViewer):

std::atomic<bool>    mIsLoaded; 
std::atomic<bool>    mIsVisible; 
std::mutex      mImageMutex; 

然后纹理访问例程如下:

void ContentViewer::setTexture(ci::gl::TextureRef texture) 
{ 
    std::lock_guard<std::mutex> guard(mImageMutex); 
    mImage = texture; 
} 

ci::gl::TextureRef ContentViewer::getTexture() 
{ 
    std::lock_guard<std::mutex> guard(mImageMutex); 
    if (mIsVisible) 
    { 
     if (mImage != nullptr) 
     { 
      mIsLoaded = true; 
      return mImage; 
     } 
     mIsLoaded = false; 
    } 
    return nullptr; 
} 

纹理加载器可能会从主线程一次接收到许多纹理加载请求,然后通过队列加载并将纹理分配给纹理加载请求消息中指向的内容查看器。

我遇到的问题是当主线程“删除”内容查看器时,纹理加载线程可能在其队列中有未完成的请求,并且在处理它时,内容查看器已经删除和程序崩溃。

我不知道如何去除纹理线程工作队列中的优秀纹理加载请求。我不能让主线程等待为内容查看器加载相关纹理,但是,那么实现此目的的最佳实践策略是什么?

感谢 - Laythe

+0

会使用std :: shared_ptr会有帮助吗? – xaxxon

+0

我已经对opengl-es的问题进行了修改。 (该opengl标签意味着桌面gl)。如果你的意思是desktop-gl,可以随意标记,并删除opengl-es标签。 – BDL

+0

你能分享你的纹理工作线程的功能吗? –

回答

0

好了,有两种选择恕我直言,无论是:

  1. 你等特定对象的所有要求删除之前完成。
  2. 在执行任何预定的操作之前,检查对象是否仍然存在。

我对您的应用程序没有足够的了解:如何实施队列以及为什么以及何时安排请求,因此我无法对此提供任何反馈。

+0

嗨,我等不及要求被尊重。该代码用于执行关闭操作,因此期望的效果应该是取消未完成的请求。你提到的第二个选项是我的第一次尝试,我发现如果我检查有效性,然后使用该对象,它可能已经消失在检查和使用之间,因此崩溃。我确实找到了一个我将在下面概述的解决方案。谢谢你的帮助! – Laythe

+0

在多线程系统中,您有__要锁定对象以防止出现这些情况,例如在检查有效性后删除对象。否则,不可能阻止你描述的情况。所以实例的删除方法也必须锁定mImageMutex。你的问题是多线程中常见的问题。像这里所说的[链接] http://stackoverflow.com/questions/12455297/delete-an-object-securely-from-a-multi-threaded-program:你不能删除一个正在使用的对象。没有大量的互斥体可以解决这个问题。 – JHBonarius

0

我发现我需要构建一个std :: mutex保护向量的取消列表。当主线程想要退出时,只需向该向量中添加一个条目并继续。纹理加载器线程有额外的负担来检查每个接收到的纹理请求的列表,但操作不在关键路径上。

我仍然对替代品/建议感兴趣。

线程的小外形低于:

void textureLoaderThreadFn() 
{ 
    log("texture loader thread started"); 

    while (!mShouldQuit) 
    { 
     // Wait for texture loader request 
     TextureLoaderRequest *textureLoaderRequest = nullptr; 
     mTextureRequests->popBack(&textureLoaderRequest); 

     // it is possible popBack didnt modify textureLoaderRequest (eg. when cancelled on exit) 
     if (textureLoaderRequest != nullptr) 
     { 
      std::lock_guard<std::mutex> lk(mCancellationListMutex); 

      if (std::find(mCancellationList.begin(), mCancellationList.end(), textureLoaderRequest->mFilename) != mCancellationList.end()) 
      { 
       // Cancelled 

       // we must reset the isLoading that was set by the main thread, 
       // so that the request to load the texture can get put back if need be 
       textureLoaderRequest->mContentViewer->mIsLoading = false; 

       // remove from cancellation list 
       mCancellationList.erase(std::remove(mCancellationList.begin(), mCancellationList.end(), textureLoaderRequest->mFilename), mCancellationList.end()); 
      } 
      else 
      { 
       // Not cancelled 
       <SERVICE TEXTURE REQUEST> 
      } 

      // dont need this anymore 
      delete textureLoaderRequest; 
     } 
    } 
    log("texture loader thread stopped"); 

    // Empty the queue  
    int count = 0; 
    TextureLoaderRequest *textureLoaderRequest = nullptr; 
    while (mTextureRequests->tryPopBack(&textureLoaderRequest)) 
    { 
     if (textureLoaderRequest != nullptr) 
      delete textureLoaderRequest; 
     count++; 
    } 
    log("texture loader thread purged " + std::to_string(count) + " outstanding texture load requests"); 
} 
0

我会建议你的状态标志添加到内容查看器来区分3个状态;预定着色,被着色并且没有预定着色。 主线程只有在计划着色或未计划着色时才应删除内容查看器。

纹理工作线程将状态更改为彩色,并且一旦它被着色就将其置为彩色。

状态的改变和状态的检查,如果它可以被删除,应该总是由相同的互斥体范围;你可能会把标志设置为内容查看器中的私有标志,并使用两个公共方法1)void change_status(status)和2)bool can_delete()。

这两个函数都应该从获取相同的互斥量开始。 1)用于主线程和纹理工作线程中的不同转换,以及2)主线程在删除内容查看器之前仅在状态未着色时才返回true。

在纹理工作线程中,在退出之前,如果主线程没有删除最后一个有色内容(因为它可能处于状态被着色),您可能会删除最后一个有色内容。

+0

主线程不能删除它,因为它的状态是“正在着色”; can_delete将返回false。然后在退出纹理线程时删除最后一个... –

+0

嗨,如果主线程想要在执行预定请求之前关闭内容查看器,那么迄今为止我唯一想出的方案是创建一个注释我们删除了内容查看器(我在下面提到的互斥保护取消列表),然后让纹理加载器根据请求目标地址在取消列表中取消纹理请求。这是我在下面概述的选项,似乎可行,但没有经过压力测试。我不知道为什么我们需要用std:mutex保护原子布尔标志? – Laythe

+0

你的意思是从纹理加载程序线程本身中删除内容查看器? – Laythe