【发布时间】:2018-06-20 13:57:26
【问题描述】:
我有一个读取大量文件的代码。一些文件可以被缓存。消费者在请求文件时收到shared_ptr。如果文件仍在内存中,其他消费者可以请求此文件并从缓存中获取它。如果文件不在内存中,则将其加载并放入缓存中。
简化代码:
struct File
{
File(std::string);
bool AllowCache() const;
};
typedef std::shared_ptr<File> SharedPtr;
typedef std::weak_ptr<File> WeakPtr;
std::map<std::string, WeakPtr> Cache;
SharedPtr GetFile(std::wstring Name)
{
auto Found = Cache.find(Name);
if (Found != Cache.end())
if (auto Exist = Found->second.lock())
return Exist;
auto New = boost::make_shared<File>(Name);
if (New->AllowCache())
Cache[Name] = New;
return New;
}
我的问题是:如何使这段代码安全?即使我通过互斥锁保护GetFile() 的内容,它仍然可以从weak_ptr::lock() 返回非空指针,而其他线程正在运行指向File 对象的析构函数。
我看到了一些解决方案,例如:
- 将
shared_ptrs 存储在缓存中并运行一个单独的线程,该线程将 不断删除shared_ptr-s 和use_count()==1(我们称之为Cleanup())。 - 将
shared_ptrs 存储在缓存中,并要求消费者使用shared_ptr<File>的特殊包装器。这个包装器将有shared_ptr<File>作为成员,并将reset()它在析构函数中,然后调用Cleanup()。
第一种解决方案有点矫枉过正。第二种解决方案需要重构我项目中的所有代码。这两种解决方案都对我不利。有没有其他方法可以使它安全?
【问题讨论】:
-
作为文体旁注:请重新考虑对变量和函数名称使用大写字母。它使变量和函数乍一看像类型/声明,这使得阅读代码变得更加困难(至少对我而言)。在 C++ 中命名 anything
New相当...粗体(我花了 30 年代思考“新的?新的什么?你错过了指针分配的类型吗?”在我意识到New之前一个变量...) -
您为什么不直接使用文件的共享内存映射而不自己担心所有这些?
-
正如您所指出的,您可以使用简单的互斥锁使您的缓存线程安全。真正的问题是如何使访问从缓存线程获得的共享对象安全?这是一个独立于缓存本身的问题。
-
"即使我通过互斥锁保护 GetFile() 的内容,它仍然可以从 weak_ptr::lock() 返回非空指针,而其他线程正在运行指向 File 对象的析构函数." - 你确定吗?我认为
std::shared_ptr会在破坏其目标之前阻止std::weak_ptr访问。 -
@CharonX 这对我来说一开始看起来像虚幻引擎风格,他们有大写变量命名......但在通常的 C++ 代码中这有点令人困惑
标签: c++ multithreading shared-ptr