2017-02-05 201 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