延迟初始化缓存...如何使其线程安全?
这就是我所拥有的:
- Windows 服务
- C#
- 多线程
- 服务使用读-写-锁(一次多次读取,写入会阻塞其他读/写线程)
- 一个简单的自写 DB
- C++
- 足够小以适合内存
- 足够大,不想在启动时加载它(例如 10GB)
- 读取性能非常重要
- 写作不太重要
- 树形结构
- 树节点中保存的信息存储在文件中
- 为了提高性能,文件仅在第一次使用和缓存时加载
- 延迟初始化可加快数据库启动速度
由于数据库将非常频繁地访问这些节点信息(每秒数千次)并且由于我不经常写入,因此我想要使用某种双检查锁定模式。
我知道这里有很多关于双重检查锁定模式的问题,但似乎有很多不同的意见,所以我不知道什么最适合我的情况。你会用我的设置做什么?
举个例子:
- 一棵有 100 万个节点的树
- ,每个节点存储一个键值对列表(存储在文件中用于持久化,文件大小大小:10kB),
- 当第一次访问节点时,会加载并存储该列表在地图(比如 std::map)中,
- 下次访问此节点时,我不必再次加载文件,我只需从地图中获取它。
- 唯一的问题:两个线程第一次同时访问该节点并且想要 写入缓存映射。这种情况发生的可能性很小,但也并非不可能。这就是我需要线程安全的地方,这不应该花费太多时间,因为我通常不需要它(特别是,一旦整个数据库都在内存中)。
that's what I have:
- a Windows Service
- C#
- multithreaded
- the service uses a Read-Write-Lock (multiple reads at one time, writing blocks other reading/writing threads)
- a simple, self-written DB
- 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
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?
Here's an example:
- 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).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
关于双重检查锁定:
它不是线程安全的,因为您检查资源的地址是否为空。因为在初始化指向它的资源对象之前,资源指针有可能被分配给一个非空值。
但是使用 C++11 的“原子”功能,您可能会拥有双重检查锁定机制。
编辑:没有原子
MemoryBarrier()
确保首先创建dummy
然后分配给resource
。根据此链接指针分配将在 x86 和 x64 系统中是原子的。并且
易失性
确保resource
的值不会被缓存。About double checked locking:
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.
But with the "atomics" feature of C++11 you may have a doubly checked locking mechanism.
EDIT: Without atomics
MemoryBarrier()
ensures thatdummy
will be first created then assigned toresource
.According to this link pointer assignments will be atomic in x86 and x64 systems. And
volatile
ensures that the value ofresource
will not be cached.您是在问如何使读取数据库或读取节点线程安全吗?
如果您尝试后者并且不经常写作,那么为什么不让您的节点 不可变,就这样?如果您需要编写某些内容,请从现有节点复制数据,修改它并创建另一个节点,然后将其放入数据库中。
Are you asking how to make reading the DB or reading the Nodes thread safe?
If you're trying to the latter and you're not writing very often, then why not make your nodes immutable, period? If you need to write something, then copy the data from the existing node, modify it and create another node which you can then put in your database.