Lazy初始化缓存...我如何使它线程安全? [英] Lazy initialized caching... how do I make it thread-safe?
问题描述
这是我的:
- 一个Windows服务
- li>
- 多线程
- 该服务使用读写锁定(多次读取,写入其他读取/写入线程)
- C ++
-
- 读取性能非常重要
- 大到足以不想在启动时加载它(例如10GB) li>
- 树形结构
- 树形节点中保存的信息存储在文件中
- 为了更快的性能,文件只在第一次使用和缓存时加载
- 延迟初始化以加快数据库启动速度
- C++
- small enough to fit into memory
- big enough not wanting to load it on startup (e.g. 10GB)
- read-performance is very important
- writing is less important
- tree structure
- informations held in tree nodes are stored in files
- for faster performance, the files are loaded only the first time they are used and cached
- lazy initialization for faster DB startup
由于DB经常访问这些节点信息(每秒数千次)
As the DB will access those node informations very often (in the magnitude of several thousand times a second) and as I don't write very often, I'd like to use some kind of double checked locking pattern.
我知道这里有很多关于双重锁定模式的问题,但似乎有这么多不同的意见,所以我不知道什么是最好的我的情况。
I know there is many questions about the double checked locking pattern here, but there seems to be so many different opinions, so I don't know what's the best for my case. What would you do with my setup?
以下是一个例子:
- a每百万个节点的树
- 每个节点存储一个键/值对列表(存储在文件中,用于持久性,文件大小:10kB)
- 当第一次访问节点时,列表被加载并存储在映射(sth。类似std :: map)
- 下次访问此节点时,
- 只有问题:两个线程第一次同时访问节点,想要
写入到高速缓存映射。这是不太可能发生,但这不是不可能的。这是我需要线程安全,这不应该花太多时间,因为我通常不需要它(特别是,一旦整个DB在内存中)。
- a tree with 1 million nodes
- every node stores a list of key-value-pairs (stored in a file for persistence, file size magnitude: 10kB)
- when accessing a node for the first time, the list is loaded and stored in a map (sth. like std::map)
- the next time this node is accessed, I don't have to load the file again, I just get it from the map.
- only problem: two threads are simultaneously accessing the node for the first time and want to write to the cache-map. This is very unlikely to happen, but it is not impossible. That's where I need thread-safety, which should not take too much time, as I usually don't need it (especially, once the whole DB is in memory).
推荐答案
关于双重锁定:
class Foo { Resource * resource; Foo() : resource(nullptr) { } public: Resource & GetResource() { if(resource == nullptr) { scoped_lock lock(mutex); if(resource == nullptr) resource = new Resource(); } return *resource; } }
它不是线程安全的,因为您检查资源地址为空。因为在初始化指向它的Resource对象之前,有一个资源指针被分配给一个非空值的机会。
It is not thread-safe as you check whether the address of resource is null. Because there is a chance that resource pointer is assigned to a non-null value right before the initializing the Resource object pointed to it.
但是使用atomics C ++ 11你可能有一个双重检查锁定机制。
But with the "atomics" feature of C++11 you may have a doubly checked locking mechanism.
class Foo { Resource * resource; std::atomic<bool> isResourceNull; public: Foo() : resource(nullptr), isResourceNull(true) { } Resource & GetResource() { if(isResourceNull.load()) { scoped_lock lock(mutex); if(isResourceNull.load()) { resource = new Resoruce(); isResourceNull.store(false); } } return *resource; } }
编辑:无原子
#include <winnt.h> class Foo { volatile Resource * resource; Foo() : resource(nullptr) { } public: Resource & GetResource() { if(resource == nullptr) { scoped_lock lock(mutex); if(resource == nullptr) { Resource * dummy = new Resource(); MemoryBarrier(); // To keep the code order resource = dummy; // pointer assignment } } return *const_cast<Resource*>(resource); } }
MemoryBarrier code>确保将首先创建
dummy
,然后将其分配给resource
。
根据此链接指针赋值在x86和x64系统中是原子的。volatile
可确保resource
的值不会被缓存。MemoryBarrier()
ensures thatdummy
will be first created then assigned toresource
. According to this link pointer assignments will be atomic in x86 and x64 systems. Andvolatile
ensures that the value ofresource
will not be cached.这篇关于Lazy初始化缓存...我如何使它线程安全?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
- li>