2011-06-10 123 views
3

事情似乎有效,但我不確定這是否是最好的方式。從另一個線程修改向量中的指針數據是否安全?

基本上我有一個異步檢索數據的對象。該對象具有在主線程上分配和解除分配的指針向量。使用boost函數,一個進程結果回調與這個向量中的一個指針綁定。當它觸發時,它將在一些任意線程上運行並修改指針的數據。

現在我有部分圍繞推入矢量和擦除以防異步檢索對象收到更多請求的情況下的關鍵部分,但我想知道是否需要在修改指針的回調中使用某種警戒數據也是如此。

希望這個瘦身僞代碼使事情變得更加清晰:

class CAsyncRetriever 
{ 
    // typedefs of boost functions 

    class DataObject 
    { 
     // methods and members 
    }; 

public: 
    // Start single asynch retrieve with completion callback 
    void Start(SomeArgs) 
    { 
     SetupRetrieve(SomeArgs); 
     LaunchRetrieves(); 
    } 

protected: 
    void SetupRetrieve(SomeArgs) 
    { 
      // ... 

     { // scope for data lock 
      boost::lock_guard<boost::mutex> lock(m_dataMutex); 
      m_inProgress.push_back(SmartPtr<DataObject>(new DataObject))); 
      m_callback = boost::bind(&CAsyncRetriever::ProcessResults, this, _1, m_inProgress.back()); 
     } 

      // ... 
    } 

    void ProcessResults(DataObject* data) 
    { 
       // CALLED ON ANOTHER THREAD ... IS THIS SAFE? 
     data->m_SomeMember.SomeMethod(); 
       data->m_SomeOtherMember = SomeStuff; 
    } 

    void Cleanup() 
    { 
       // ... 

     { // scope for data lock 
      boost::lock_guard<boost::mutex> lock(m_dataMutex); 
      while(!m_inProgress.empty() && m_inProgress.front()->IsComplete()) 
       m_inProgress.erase(m_inProgress.begin()); 
     } 

       // ... 
     } 

private: 
    std::vector<SmartPtr<DataObject>> m_inProgress; 
    boost::mutex m_dataMutex; 
     // other members 
}; 

編輯:這是ProccessResults回調的實際代碼(加上您的利益評論)

void ProcessResults(CRetrieveResults* pRetrieveResults, CRetData* data) 
     { 
// pRetrieveResults is delayed binding that server passes in when invoking callback in thread pool 
// data is raw pointer to ref counted object in vector of main thread (the DataObject* in question) 

       // if there was an error set the code on the atomic int in object 
      data->m_nErrorCode.Store_Release(pRetrieveResults->GetErrorCode()); 

       // generic iterator of results bindings for generic sotrage class item 
      TPackedDataIterator<GenItem::CBind> dataItr(&pRetrieveResults->m_DataIter); 
       // namespace function which will iterate results and initialize generic storage 
      GenericStorage::InitializeItems<GenItem>(&data->m_items, dataItr, pRetrieveResults->m_nTotalResultsFound); // this is potentially time consuming depending on the amount of results and amount of columns that were bound in storage class definition (i.e.about 8 seconds for a million equipment items in release) 
       // atomic uint32_t that is incremented when kicking off async retrieve 
      m_nStarted.Decrement(); // this one is done processing 

       // boost function completion callback bound to interface that requested results 
      data->m_complete(data->m_items); 
     } 
+0

如果您希望得到明智的答案,您需要添加更多信息。目前很重要的一點是缺失,特別是'SomeMethod','IsComplete'是什麼以及如何實現以及* complete *標誌如何產生。如果處理函數的最後一行是一個賦值,除非這是用戶定義的類型,並且它被鎖定,並且它是將「IsComplete」設置爲true的那個,否則答案是否定的,這是不安全的。但我的猜測是,這個處理函數只是一個骨架。 – 2011-06-10 23:03:15

+0

是啊,這是一個很大的框架,我只是想表明它調用方法並在其中指定成員。在這一點上,我認爲只要每個指針的每個回調只有一個線程的語義保持不變,我就會好起來的。增量異步檢索引入了太多的延遲,所以我並不擔心該實現。 – AJG85 2011-06-10 23:08:59

+0

這種方法本身並沒有什麼錯,但魔鬼在細節中。根據實際操作是什麼以及如何對* shared *數據執行同步(我的猜測是共享數據只是一個由'IsComplete'檢查的標誌),那麼它可能是正確的。無論是最好的方法還是可以簡化的方法都是一個不同的問題,但沒有上下文就無法說清楚。可能很重要的事情是:'SmartPtr'線程安全的實現?那裏的競賽條件很有可能。 – 2011-06-11 10:10:37

回答

2

不,不安全。

ProcessResults對通過DataObject傳遞給它的數據結構進行操作。它表明你已經在不同線程之間共享狀態,如果兩個線程同時在數據結構上運行,你可能會遇到一些麻煩。

+0

每個檢索只觸發一個回調。所以在任何時候只有一個線程訪問指針。如果你提出了5個請求,它會分配5個對象,5個綁定,啓動它們,另外5個線程的5個回調將被調用來執行骯髒的工作,然後在下次你提出更多請求時清理事件完成。 – AJG85 2011-06-10 20:47:19

+0

「完成」由回調觸發確定,完成它的處理,然後在執行結束時觸發完整的回調。 – AJG85 2011-06-10 21:11:15

+0

啊,在回調中完成的任務類型「暗示」您使用'DataObject'來傳遞_another_結構,以便回調方法可以處理它。你會用更多關於回調方法的語義的更多細節來更新問題,即它在它的工作方式上是什麼,它是在線程之間共享的嗎? – 2011-06-11 02:59:21

3

現在看來,Cleanup代碼可能會銷燬一個對象,其中的ProcessResults回調處於運行狀態。當您在回調中刪除指針時會導致問題。

我的建議是您擴展m_dataMutex的語義涵蓋回調,但如果回調是長期運行,或可在SetupRetrieve內嵌發生(有時會發生這種情況 - 雖然在這裏你的狀態回調是一個不同的線程,在這種情況下你可以)然後事情就更復雜了。目前m_dataMutex對於是否控制對矢量或其內容的訪問有點困惑。在明確其範圍後,ProcessResults可以被增強以驗證鎖中有​​效載荷的有效性。

+0

這就是IsComplete方法檢查沒有擔心那裏...我想鎖定在回調中,但是這些在任意時間被任意線程調用,因爲服務器完成異步任務。鎖定在一個互斥鎖上同步,從而形成一個瓶頸,而目前的回調可以並行完成。 – AJG85 2011-06-10 20:40:03

+0

@ AJG85 - 因爲你的回調得到了一個原始指針,所以在使用它之前,你必須確認它仍然有效。你能否將智能指針轉換爲ref-counting類型,並且只鎖定向量來檢查它是否正常並獲得對該對象的安全引用?然後,大部分回調邏輯仍然可以並行進行。另一個問題是,回調中對象的操作是否會干擾其他線程的邏輯,如果是這樣的話,這又是一個問題。 – 2011-06-10 20:41:47

+0

我遺漏了空檢查和大部分看起來不相關的代碼。此外,「SmartPtr」的名稱不同,但它是一個與STL容器兼容的引用計數對象(可能在某一天有利於C++ 0x)...有一個弱指針的朋友類I可能可以用來獲取引用,但變化的生命需要超出回調和線程的範圍。也沒有別的東西可以看到或使用私人對象,直到處理結果回調,當它觸發完成回調。 – AJG85 2011-06-10 21:07:46

0

更新指針應該是一個原子操作,但您可以使用InterlockedExchangePointer(在Windows中)來確保。不知道Linux的等價物是什麼。

那麼唯一的考慮就是如果一個線程正在使用一個過時的指針。另一個線程是否刪除原始指針指向的對象?如果是這樣,你有一個確定的問題。

+0

它不會更新傳遞給它的數據結構上的指針「ProcessResults」_operates_。 – 2011-06-10 20:32:55

+0

@Khaled:這是一個指針,但它確實調用方法並更改指針指向的對象的成員。 – AJG85 2011-06-10 20:41:07

+0

所以我需要使用一個原子指針?我不需要交換或更改指針指向的內容就可以使用它。此外,其他線程不會刪除任何內容,因爲釋放在另一個線程上創建的內容聽起來像一週中的任何一天的壞消息。 – AJG85 2011-06-10 20:44:13

相關問題