2016-02-25 56 views
1

我試圖想出一個解決以下問題的快速方法:這是線程間對象共享策略的聲音嗎?

我有一個線程產生數據,並有幾個線程使用它。我不需要對生成的數據進行排隊,因爲數據的生成速度比消耗的要慢得多(即使偶爾出現這種情況,如果偶爾跳過數據點也不會有問題)。所以,基本上,我有一個封裝了「最近狀態」的對象,它只允許生產者線程更新。

我的策略如下(請讓我知道如果我完全把我的搖桿):

我創建三類這個例子:Thing(實際狀態對象),SharedObject<Thing>(對象可以是每個線程本地的,並且可以讓該線程訪問底層的Thing)和SharedObjectManager<Thing>,它包含shared_ptr以及mutex

SharedObjectManager(SOM)的實例是一個全局變量。 當製片人開始時,它實例化一個Thing,並告訴全局SOM。然後製作一份副本,並完成所有更新該副本的工作。當它準備提交對Thing的更改時,它會將新Thing傳遞給全局SOM,這會鎖定它的互斥鎖,更新它保留的共享指針,然後釋放該鎖。

同時,消費者線程全部內核化SharedObject<Thing>。這些對象每個都保持一個指向全局SOM的指針,以及由SOM保存的shared_ptr的緩存副本......它保持該緩存直到明確調用update()

我相信這是越來越難走,所以這裏的一些代碼:

#include <mutex> 
#include <iostream> 
#include <memory> 

class Thing 
{ 
    private: 
     int _some_member = 10; 
    public: 
     int some_member() const { return _some_member; } 
     void some_member(int val) {_some_member = val; } 
}; 

// one global instance 
template<typename T> 
class SharedObjectManager 
{ 
    private: 
     std::shared_ptr<T> objPtr; 
     std::mutex objLock; 

    public: 
     std::shared_ptr<T> get_sptr() 
     { 
      std::lock_guard<std::mutex> lck(objLock); 
      return objPtr; 
     } 

     void commit_new_object(std::shared_ptr<T> new_object) 
     { 
      std::lock_guard<std::mutex> lck (objLock); 
      objPtr = new_object; 
     } 
}; 


// one instance per consumer thread. 
template<typename T> 
class SharedObject 
{ 
    private: 
     SharedObjectManager<T> * som; 
     std::shared_ptr<T> cache; 

    public: 
     SharedObject(SharedObjectManager<T> * backend) : som(backend) 
     {update();} 

     void update() 
     { 
      cache = som->get_sptr(); 
     } 

     T & operator *() 
     { 
      return *cache; 
     } 

     T * operator->() 
     { 
      return cache.get(); 
     } 


}; 



// no actual threads in this test, just a quick sanity check. 

SharedObjectManager<Thing> glbSOM; 

int main(void) 
{ 
    glbSOM.commit_new_object(std::make_shared<Thing>()); 

    SharedObject<Thing> myobj(&glbSOM); 

    std::cout<<myobj->some_member()<<std::endl; 
    // prints "10". 
} 

由生產者線程使用的理念是:

// initialization - on startup 
auto firstStateObj = std::make_shared<Thing>(); 
glbSOM.commit_new_object(firstStateObj); 

// main loop 
while (1) 
{ 
    // invoke copy constructor to copy the current live Thing object 
    auto nextState = std::make_shared<Thing>(*(glbSOM.get_sptr())); 

    // do stuff to nextState, gradually filling out it's new value 
    // based on incoming data from other sources, etc. 
    ... 

    // commit the changes to the shared memory location 
    glbSOM.commit_new_object(nextState); 
} 

受到消費者的使用將是:

SharedObject<Thing> thing(&glbSOM); 
while(1) 
{ 
    // think about the data contained in thing, and act accordingly... 
    doStuffWith(thing->some_member());   

    // re-cache the thing 
    thing.update(); 
} 

謝謝!

+0

'std :: shared_ptr objPtr;'有我害怕在那裏有某個地方有另一方使用'* objPtr'沒有適當的保護。怕我不得不說。 – user4581301

+0

聽起來很複雜。取而代之的是在限制大小的生產者中使用一個隊列,只保存有效的(提交)對象並允許丟棄元素。這可能會更簡單(擺脫無用的經理)。 –

回答

1

這是過度工程的方式。相反,我會建議做以下事情:

  • 創建一個指針Thing* theThing與保護互斥一起。無論是全球性的,還是通過其他方式共享的。將其初始化爲nullptr。
  • 在你製作:Thing類型的使用兩個本地對象 - Thing thingOneThing thingTwo(記住,thingOne沒有比thingTwo好,但一個被稱爲thingOne是有原因的,但是這是一個東西的事當心貓。) 。從填充thingOne開始。完成後,鎖定互斥鎖,將thingOne地址複製到theThing,解鎖互斥鎖。開始填充thingTwo。完成後,請參閱上文。重複直到死亡。
  • 在每個監聽器中:(確保指針不是nullptr)。鎖定互斥鎖。複製由theThing指向的兩個對象。解鎖互斥鎖。與你的副本一起工作。閱後即焚。重複直到死亡。
+0

絕對要小心貓。他想要你所有的基地。 – user4581301

+0

@ user4581301,我們其中一個沒有得到這個笑話。它會是誰? – SergeyA

+0

如果因爲某種原因複製對象出現問題,您可以使用由互斥鎖保護的'shared_ptr '。或者,如果只有一個線程需要消耗該對象,則可以使用在互斥體保護下移入/移出的'unique_ptr '。 –