C++ 这种线程间对象共享策略合理吗?
我试图想出一个快速解决以下问题的方法: 我有一个线程产生数据,还有几个线程使用数据。我不需要对生成的数据排队,因为数据生成的速度比消耗的速度慢得多(即使偶尔出现这种情况,如果偶尔跳过数据点也不会有问题)。因此,基本上,我有一个封装“最新状态”的对象,它只允许生产者线程更新 我的策略如下(如果我完全疯了,请告诉我): 我为这个例子创建了三个类:C++ 这种线程间对象共享策略合理吗?,c++,multithreading,thread-safety,shared-memory,C++,Multithreading,Thread Safety,Shared Memory,我试图想出一个快速解决以下问题的方法: 我有一个线程产生数据,还有几个线程使用数据。我不需要对生成的数据排队,因为数据生成的速度比消耗的速度慢得多(即使偶尔出现这种情况,如果偶尔跳过数据点也不会有问题)。因此,基本上,我有一个封装“最新状态”的对象,它只允许生产者线程更新 我的策略如下(如果我完全疯了,请告诉我): 我为这个例子创建了三个类:Thing(实际状态对象)、SharedObject(一个对象,可以是每个线程的本地对象,并允许该线程访问底层对象)和SharedObjectManager
Thing
(实际状态对象)、SharedObject
(一个对象,可以是每个线程的本地对象,并允许该线程访问底层对象)和SharedObjectManager
,它封装了一个共享的ptr
和一个互斥对象
SharedObjectManager
(SOM)的实例是一个全局变量。
当producer启动时,它实例化一个对象,并将其告知全局SOM。然后,它制作一个副本,并对该副本执行所有更新工作。当它准备提交它对对象的更改时,它将新对象传递给全局SOM,全局SOM锁定它的互斥体,更新它保留的共享指针,然后释放锁
同时,使用者线程都是INTSANTATESharedObject
。这些对象各自保留一个指向全局SOM的指针,以及由SOM保留的共享\u 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".
}
#包括
#包括
#包括
阶级事务
{
私人:
int(某些)成员=10 ;;
公众:
int some_member()常量{return_some_member;}
void some_member(int val){some_member=val;}
};
//一个全局实例
模板
类SharedObjectManager
{
私人:
std::共享对象;
std::互斥对象锁;
公众:
std::shared_ptr get_sptr()
{
标准:锁紧装置lck(objLock);
返回objPtr;
}
void commit_new_对象(std::shared_ptr new_对象)
{
标准:锁紧装置lck(objLock);
objPtr=新的_对象;
}
};
//每个使用者线程一个实例。
模板
类共享对象
{
私人:
SharedObjectManager*som;
std::共享的ptr缓存;
公众:
SharedObject(SharedObjectManager*后端):som(后端)
{update();}
无效更新()
{
cache=som->get_sptr();
}
T&运算符*()
{
返回*缓存;
}
T*运算符->()
{
返回cache.get();
}
};
//在这个测试中没有实际的线程,只是一个快速的健全性检查。
共享项目经理glbSOM;
内部主(空)
{
提交新对象(std::make_shared());
共享对象myobj(&glbSOM);
std::cout这是过度工程化的。相反,我建议做以下几点:
- 创建一个指向
Thing*theThing
的指针以及保护互斥锁。可以是全局指针,也可以通过其他方式共享。将其初始化为null ptr
- 在你的制作人中:使用两个
Thing
类型的本地对象-thingOne
和thingTwo
(记住,thingOne
并不比thingTwo
好,但是其中一个被称为thingOne
是有原因的,但这是一个东西。小心猫。)。首先填充thingOne
。完成后,锁定互斥锁,将thingOne
地址复制到thingOne
,解锁互斥锁。开始填充thingTwo
。完成后,请参见上文。重复操作,直到被杀死
- 在每个侦听器中:(确保指针不是null ptr)。锁定互斥体。复制由
theThing
指向的两个对象。解锁互斥体。处理您的副本。读取后刻录。重复此操作,直到终止
std::shared\u ptr objPtr;
让我担心,在某个地方,有另一方在没有适当保护的情况下使用*objPtr
。我不得不说不。听起来很复杂。相反,在生产者中使用一个大小有限的队列,只持有有效(已提交)对象并允许删除元素。这可能更简单(摆脱无用的管理器)。一定要小心猫。他想要你所有的基础。@user4581301,我们中的一个没有得到这个笑话。会是谁?如果复制对象是出于某种原因的问题,你可以使用受互斥锁保护的共享\u ptr
。或者如果只有一个线程需要使用该对象,你可以使用你移入/移出的唯一\u ptr
保护互斥。不确定@SergeyA。可能是两条鱼中的一条。我猜是红色的。@DavidSchwartz,应该复制或移动对象,否则,设计会崩溃。
// 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();
}