1

それは私が持っているものです:

  • Windowsサービス
    • C#
    • マルチスレッド
    • サービスはRead-Write-Lockを使用します(一度に複数の読み取り、書き込みは他の読み取り/書き込みスレッドをブロックします)
  • シンプルな自作のDB
    • C ++
    • メモリに収まるほど小さい
    • 起動時にロードしたくない十分な大きさ(例:10GB)
    • 読み取りパフォーマンスは非常に重要です
    • 書くことはそれほど重要ではありません
    • 木の構造
    • ツリーノードに保持されている情報はファイルに保存されます
    • パフォーマンスを向上させるために、ファイルは最初に使用およびキャッシュされたときにのみロードされます
    • DBの起動を高速化するための遅延初期化

DBはこれらのノード情報に非常に頻繁に(1秒間に数千回の大きさで)アクセスし、私はあまり頻繁に書き込まないので、ある種のダブルチェックロックパターンを使用したいと思います。

ここでダブルチェックロックパターンについて多くの質問があることは知っていますが、非常に多くの異なる意見があるように思われるので、私の場合に何が最善かわかりません。私のセットアップで何をしますか?

次に例を示します。

  • 100万ノードのツリー
  • すべてのノードは、キーと値のペアのリストを格納します(永続性のためにファイルに格納され、ファイルサイズの大きさ:10kB)
  • 初めてノードにアクセスするとき、リストはマップにロードおよび保存されます(sth。like std :: map)
  • 次にこのノードにアクセスするときに、ファイルを再度ロードする必要はありません。マップから取得するだけです。
  • 唯一の問題:2つのスレッドが初めてノードに同時にアクセスしていて、キャッシュマップに書き込みたい。これが発生する可能性は非常に低いですが、不可能ではありません。ここでスレッドセーフが必要になりますが、通常は必要ないので(特に、DB全体がメモリに格納されたら)、それほど時間はかかりません。
4

2 に答える 2

4

ダブルチェックロックについて:

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;
  }
}

リソースのアドレスがnullかどうかを確認するため、スレッドセーフではありません。リソースポインタが指すリソースオブジェクトを初期化する直前に、リソースポインタがnull以外の値に割り当てられる可能性があるためです。

しかし、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がキャッシュされないようにします。

于 2011-11-14T10:35:00.973 に答える
1

DBの読み取りまたはノードスレッドの読み取りを安全にする方法を尋ねていますか?

後者を試みていて、あまり頻繁に記述していない場合は、ノードを不変の期間にしてみませんか?何かを書き込む必要がある場合は、既存のノードからデータをコピーして変更し、データベースに配置できる別のノードを作成します。

于 2011-11-11T17:13:27.910 に答える