我只是想知道如何使用C98解决这个问题(所以没有shared_ptr
):
我的班级很大,有很多数据(BigData
)
我有一个DataStorage
类,可以在map
中跟踪该数据
数据不太可能更改,但是可以
我在多线程环境中
class BigData;
class DataStorage{
public:
const BigData *getStuff(int which_one) const{
lock_guard<mutex> guard(mut);
return &myReallyBigDatas[which_one]; // thanks Donghui
}
protected:
mutable mutex mut;
map<int,BigData> myReallyBigDatas;
}
(如Keith M所建议。我没有提到我要解决的问题)
我知道这段代码是错误的,从我的角度来看,我想解决两个主要问题:
如果由于删除或覆盖了地图上的此位置而使对象返回了消失,则该对象将消失(我将有一个指针指向无处,UB!)。
如果返回的BigData实例被修改
当然,我想找到一种解决方案,避免将来其他人更改此代码时出错
我想出了以下解决方案:
包括BigData的互斥量。解决问题2
更改函数以返回一个真实的对象,而不是返回一个指针(这非常好,但是缺点是在复制一个非常大的类时会降低性能)这解决了两个问题
实现我自己的shared_ptr类(不能使用C11也不能使用boost)。解决问题1
在类DataStorage上创建一个锁定/解锁(真糟糕!)。这解决了两个问题
保持错误并祈祷很多。这个....
我敢肯定,很多人在遗留代码中找到了这样的一段代码,并且我想找到最好的解决方案。
附言我知道我正在使用C11互斥锁。我的真实代码没有它,但是用这种方式编写示例代码更容易。
最佳答案
实际上,shared_ptr
和mutex
彼此非常独立,并且您可能两者都需要-shared_ptr
用于保证恰好一个资源的释放,而mutex
用于保证没有并发的读/写操作(或并发读取也取决于互斥锁的类型)。
使用shared_ptr
基本上意味着没有数据的单个所有者。尽管这可以管理(例如引用计数),但它并不总是最佳的解决方案(记住循环依赖,对weak_ptr
的需求等)-有时最好找出一个资源的单一所有者来负责在不再需要它时(例如,如果您有一个工作线程池,可能是产生其他线程的那个线程)取消分配它-当然,您必须保证它的寿命比其他线程更长,以使所有人均可访问的数据。因此,您有几个用于管理对象生命周期的选项:
在不使用整个库的情况下“借用” boost / C ++ 11标准库/ Loki /其他存在的实现中的代码(签出许可证以验证是否可以使用它们)-您可能需要对以下代码进行更改他们
编写自己的智能指针-辛苦且仅适用于专业人士-完全不建议
选择一个资源的所有者-这就是我的建议
当涉及访问冲突管理时,基本上有两种方法:
使用某种锁来管理它们(我假设您有一个可用的锁)
通过仅允许单个线程写入对象来避免它们。通常可能需要的其他请求将不得不请求“所有者线程”进行写操作。这种方法非常适合单一资源所有者,但是与典型的共享内存多线程相比,它更是一种参与者模型,因此可能很难在遗留应用程序中引入。
您可以将锁与任何树内存管理方法一起使用,单写入者方法最适合单所有者。请注意,这是范式的重大改变,可能需要大量工作来实现消息队列和工作程序之类的东西。
如果您已经拥有基础结构(队列,工作程序等),我建议您考虑采用单所有者,单编写者的方法,否则带锁的单所有者可能是一个很好的解决方案。如果您无法选择一个所有者,请从现有库中提取代码-不要自己编写代码,因为这会导致一些错误,并且在多线程环境中进行内存管理非常困难
编辑1
既然您已经澄清了问题,那么我认为答案有点过于笼统,因此我将添加更多详细信息。
您可以做的最简单的事情是返回一个BigData&
而不是BigData*
-那时没有人可以删除它(当然可以,但是实际上所有内容都在C ++中)。否则,您还可以:
仅允许一个线程使用单个BigData
实例-(例如,通过存储带有已用std::map<int, thread_id>
的信息的其他BigData
-仅在不需要从多个线程同时访问同一实例的情况下)
返回类似BigDataProxy
而不是BigData
的内容-Proxy
应该具有特殊的资源删除功能,然后由“最后一个感兴趣的人”执行-实际上,这只是shared_ptr
的特例,但可能更容易实现。
从概念上讲,Proxy
类似于(伪代码-忽略私人/公共成员等):
class BigDataProxy {
public:
BigDataProxy(data_, instanceId_): data(data_), instanceId(instanceId_) {
std::lock_guard l(data.mutex);
data.interestedThreads[instanceId].insert(this_thread::thread_id);
}
~BigDataProxy() {
std::lock_guard l(data.mutex);
data.interestedThreads[instanceId].remove(this_thread::thread_id)
if(data.interestedThreads[instanceId].empty() && data.toDelete.contains(instanceId) {
data.elems.remove(instanceId);
data.toDelete.remove(instanceId);
}
}
BigData& operator*() {
return data.elems[instanceId];
}
void remove() {
std::lock_guard l(data.mutex);
data.toDelete.add(instanceId);
}
private:
DataStorage& data;
int instanceId;
}
DataStorage
中的更改要求它看起来像这样:class DataStorage {
std::mutex mutex;
std::map<int, BigData> elems;
std::set<int> toDelete;
std::map<int, std::set<thread_id> > interested_threads;
}
请注意,这是伪代码,在这里异常处理将很困难。
关于c++ - 访问类专有的大量数据的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/40183422/