Ленивое инициализированное кэширование ... как мне сделать его потокобезопасным? - PullRequest
1 голос
/ 11 ноября 2011

вот что у меня есть:

  • Служба Windows
    • C #
    • многопоточная
    • служба использует Read-Write-Lock (многократное чтение одновременно, запись блоков других потоков чтения / записи)
  • простая, самописная БД
    • C ++
    • достаточно мала, чтобы уместитьсяв память
    • достаточно большой, не желая загружать ее при запуске (например, 10 ГБ)
    • производительность чтения очень важна
    • запись менее важна
    • деревоструктура
    • информация, хранящаяся в узлах дерева, сохраняется в файлах
    • для повышения производительности, файлы загружаются только при первом использовании и кэшируются
    • отложенная инициализация для более быстрой БДзапуск

Поскольку БД будет получать доступ к информации об этих узлах очень часто (со скоростью несколько тысяч раз в секунду), и поскольку я пишу не очень часто, я бынравится использовать какую-то двойную проверенную схему блокировки.

Я знаю тамЗдесь много вопросов о двойной проверке схемы блокировки, но, похоже, существует много разных мнений, поэтому я не знаю, что лучше для моего случая.Что бы вы сделали с моей настройкой?

Вот пример:

  • дерево с 1 миллионом узлов
  • каждый узел хранит список пар ключ-значение-значение(сохраняется в файле для сохранения, размер файла: 10 КБ)
  • при первом обращении к узлу список загружается и сохраняется на карте (sth. like std :: map)
  • при следующем обращении к этому узлу мне не нужно загружать файл снова, я просто получаю его с карты.
  • единственная проблема: два потока одновременно получают доступ к узлу в первый рази хочу написать в кеш-карту.Это вряд ли произойдет, но это не невозможно.Вот где я нуждаюсь в поточной безопасности, которая не должна занимать слишком много времени, поскольку я обычно не нуждаюсь в ней (особенно, когда вся БД находится в памяти).

Ответы [ 2 ]

4 голосов
/ 14 ноября 2011

О двойной проверке блокировки:

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, на который он указывает.

Но с функцией «атомарности» C ++ 11 у вас может быть вдвойнепроверенный механизм блокировки.

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() гарантирует, что dummy будет сначала создано, а затем присвоено resource.Согласно эта ссылка назначения указателей будет атомарной в системах x86 и x64.И volatile гарантирует, что значение resource не будет кэшировано.

1 голос
/ 11 ноября 2011

Вы спрашиваете, как сделать чтение БД или чтение потока узлов безопасным?

Если вы пытаетесь использовать последнее и не очень часто пишете, то почему бы не сделать ваши узлы неизменными , точка? Если вам нужно что-то написать, скопируйте данные из существующего узла, измените его и создайте другой узел, который вы затем сможете поместить в свою базу данных.

...