gpt4 book ai didi

c++ - 访问类私有(private)的大量数据的正确方法是什么?

转载 作者:行者123 更新时间:2023-11-27 22:48:38 25 4
gpt4 key购买 nike

我只是想知道如何用 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 的建议。我没有提到我试图解决的问题)

我知道这段代码是错误的,从我的角度来看,我想解决两个主要问题:

  1. 如果返回的对象因为 map 上的这个位置被删除或覆盖而消失(我将有一个指向任何地方的指针,UB!)
  2. 如果修改返回的BigData实例

当然,我想找到一个解决方案,避免将来其他人更改此代码时出错

我想到了这些解决方案:

  1. 在 BigData 中加入一个互斥量。这解决了问题 2
  2. 更改函数以返回一个真实的对象,而不是一个指针(这非常好,但缺点是在复制一个非常大的类时会降低性能)这解决了这两个问题
  3. 实现我自己的 shared_ptr 类(不能使用 C11 或 boost)。这解决了问题 1
  4. 在类 DataStorage 上创建锁定/解锁(真的很糟糕!)。这解决了这两个问题
  5. 保持错误并多多祈祷。这....

我很确定很多人在遗留代码中找到了这样一段代码,我想找到最好的解决方案。

附言我知道我正在使用 C11 互斥锁。我的真实代码没有它,但是用这种方式编写示例代码更容易。

最佳答案

实际上,shared_ptrmutex彼此完全独立,您可能需要两者 - shared_ptr用于保证恰好一次资源释放,而mutex用于保证没有并发读/写操作(或并发读取,取决于互斥类型)。

使用 shared_ptr基本上意味着没有数据的单一所有者。虽然这是可以管理的(例如引用计数),但它并不总是最好的解决方案(记住循环依赖,需要 weak_ptr 等)——有时最好找到一个资源所有者来负责当它不再需要时释放它(例如,如果你有一个工作线程池,它可能是产生其他线程的线程) - 当然,你必须保证它比其他人活得更久,使每个人都可以访问的数据。因此,您有多种选择来管理对象的生命周期:

  • “借用”来自 boost/C++11 标准库/Loki/some-other-existing-implementation 的代码(检查许可证以验证您是否可以使用它们)而不使用整个库 - 您可能需要对它们进行更改
  • 编写您自己的智能指针 - 很难并且仅适用于专业人士 - 完全不推荐
  • 选择资源的单一所有者 - 这是我的建议

当谈到访问冲突管理时,基本上有两种方法:

  • 使用某种锁来管理它们(我假设您有一把锁)
  • 通过只允许一个线程写入对象来避免它们。其他通常可能想要的将不得不从“所有者线程”请求写入。这种方法非常适合单个资源所有者,但它更像是一个参与者模型,而不是典型的共享内存多线程,因此可能很难在遗留应用程序中引入。

您可以将锁与任何树内存管理方法一起使用,单作者方法最适合单所有者。请注意,这是范式的重大变化,可能需要大量工作来实现诸如消息队列和工作人员之类的东西。

如果您已经拥有基础设施(队列、工作人员等),我建议您考虑单一所有者、单一作者方法,否则带锁的单一所有者可能是一个很好的解决方案。如果您无法选择单个所有者,请从现有库中提取代码 - 不要自己编写,因为您会犯一些错误,并且多线程环境中的内存管理真的很困难

编辑 1

既然你已经把问题说清楚了,我觉得回答有点太高了,所以我再补充一些细节。

您可以做的最简单的事情就是返回 BigData&而不是 BigData* - 那时没有人应该删除它(当然这是可能的,但实际上一切都在 C++ 中)。否则,您还可以:

  • 只允许单线程使用单BigData实例 -(例如,通过存储额外的 std::map<int, thread_id> 以及有关已用 BigData 的信息 - 仅当您不需要从多个线程并发访问同一实例时
  • 返回类似 BigDataProxy 的内容而不是 BigData - Proxy应该有一个特殊的资源删除功能,然后由“最后一个感兴趣的人”执行 - 这实际上只是 shared_ptr 的一个特例,但可能更容易实现。

概念上是 Proxy事情会是这样的(伪代码 - 忽略私有(private)/公共(public)成员等):

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++ - 访问类私有(private)的大量数据的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40183422/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com