这种线程间对象共享策略是否合理?

时间:2016-02-25 18:43:43

标签: c++ multithreading thread-safety shared-memory

我正试图想出一个快速解决以下问题的方法:

我有一个产生数据的线程,以及几个使用它的线程。我不需要对生成的数据进行排队,因为数据生成的速度比消耗的慢得多(即使偶尔不是这种情况,如果偶尔跳过数据点也不会有问题)。所以,基本上,我有一个封装“最新状态”的对象,只允许生产者线程更新。

我的策略如下(如果我完全脱离摇杆,请告诉我):

我为这个例子创建了三个类:SharedObject<Thing>(实际的状态对象),SharedObjectManager<Thing>(一个对象,可以是每个线程的本地对象,并赋予该线程对底层Thing的访问权限) )和shared_ptr,其中包含mutexSharedObjectManager

SharedObject<Thing>(SOM)的实例是一个全局变量。 当生产者启动时,它会实例化Thing,并告诉全局SOM。然后它制作一份副本,并对该副本进行所有更新工作。当它准备提交它对Thing的更改时,它会将新Thing传递给全局SOM,后者锁定它的互斥锁,更新它保留的共享指针,然后释放锁。

同时,消费者线程全部变为shared_ptr。这些对象每个都保留一个指向全局SOM的指针,以及由SOM保存的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();
}

消费者的使用将是:

{
    "title":"Biology",
    "content":"Egg period: 4 -6 days \n
           Eggs laid in cracks and crevices of the loose bark on the trunk \n
            Eggs: ovoid or elliptical and dirty white in colour \n
            Adult :Reddish brown in colour",
    "isSubtitle":"N"
  } 

谢谢!

1 个答案:

答案 0 :(得分:1)

这是过度工程的方式。相反,我建议做以下事情:

  • 与保护互斥锁一起创建指向Thing* theThing的指针。可以是全局的,也可以是其他方式共享的。将其初始化为nullptr。
  • 在您的制作人中:使用Thing类型的两个本地对象 - Thing thingOneThing thingTwo(请记住,thingOne并不比thingTwo好,但是因为某种原因被称为thingOne,但这是件事。请注意猫。)。首先填充thingOne。完成后,锁定互斥锁,将thingOne地址复制到theThing,解锁互斥锁。开始填充thingTwo。完成后,请参见上文。重复直至死亡。
  • 在每个监听器中:(确保指针不是nullptr)。锁定互斥锁。复制theThing指向的对象的副本。解锁互斥锁。使用您的副本。阅后即焚。重复直至死亡。