我只是想知道如何使用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.所建议的,我没有提到我试图解决的问题)
我知道这段代码是错的,从我的角度来看,我想解决两个主要问题:
当然,我想找到一个解决方案,以避免其他人改变此代码的未来错误
我提出了这些解决方案:
我很确定很多人在遗留代码中找到了这样的代码,我想找到最好的解决方案。
P.S。我知道我使用的是C11互斥锁。我的真实代码没有它,但以这种方式编写示例代码更容易。
答案 0 :(得分:2)
您的想法是正确的:保持数据私密,并提供获取数据或部分数据的getter。
您的代码中有错误。 getStuff()的签名返回指向BigData的指针,但实现返回对BigData的引用。类型不匹配。
你是对的,因为你不想制作BigData的副本。所以你有三个选择:
答案 1 :(得分:2)
实际上,shared_ptr
和mutex
相互独立,您可能需要两者 - shared_ptr
用于保证一个资源的完全释放,而mutex
是用于保证没有并发读/写操作(或并发读取,取决于互斥锁的类型)。
使用shared_ptr
基本上意味着没有单一的数据所有者。虽然这是可以管理的(例如引用计数),但它并不总是最好的解决方案(记住循环依赖关系,需要weak_ptr
等) - 有时候最好找出一个资源的单一所有者,当它不再需要时将负责解除分配(例如,如果你有一个工作线程池,它可能是产生其他线程的那个) - 当然,那么你必须保证它的寿命比其他人长,以使每个人都可以访问数据。因此,您可以使用多种方法来管理对象的生命周期:
在访问冲突管理方面,基本上有两种方法:
您可以将锁与任何树内存管理方法一起使用,单作者方法最适合单一所有者。请注意,这是范例的重大更改,可能需要执行大量工作,如消息队列和工作人员。
如果您已经拥有基础设施(队列,工作人员等),我建议您查看单一所有者,单一作者的方法,否则带锁的单一所有者可能是一个很好的解决方案。如果您无法选择单个所有者,请从现有库中提取代码 - 不要自己编写代码,因为您会犯一些错误,多线程环境中的内存管理确实< / em> hard
编辑1
既然您已经澄清了这个问题,我觉得答案有点过高,所以我会补充一些细节。
你可以做的最简单的事情是返回一个BigData&
而不是BigData*
- 没有人应该删除它(当然它是可能的,但实际上一切都在C ++中)。否则,你也可以:
BigData
实例 - (例如,通过存储有关已使用std::map<int, thread_id>
的信息的其他BigData
- 仅当您不需要并发访问时来自多个线程的同一个实例BigDataProxy
而不是BigData
之类的内容 - Proxy
应该有一个特殊的资源删除功能,然后由&#34;最后一个感兴趣的&#34执行; - 这实际上只是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;
}
请注意,这是伪代码,异常处理在这里很难。