Lazy初始化缓存...我如何使它线程安全? [英] Lazy initialized caching... how do I make it thread-safe?

查看:178
本文介绍了Lazy初始化缓存...我如何使它线程安全?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我的:


  • 一个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 that dummy will be first created then assigned to resource. According to this link pointer assignments will be atomic in x86 and x64 systems. And volatile ensures that the value of resource will not be cached.

    这篇关于Lazy初始化缓存...我如何使它线程安全?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆