私は複数のスレッドで読み込まれ、1つのスレッドのみによって書き込まれるメモリ内のデータ構造を持っています。現在、私はこのアクセスをスレッドセーフにするためにクリティカルセクションを使用しています。残念ながら、これは別のリーダーだけがアクセスしていても、リーダーをブロックする効果があります。ロックフリーの複数のリーダーシングルライター
これを解決するには、2つのオプションがあります。
- 使用TMultiReadExclusiveWriteSynchronizerの
- は、私がこれまでに以下を持っている2.について
ロックフリーのアプローチを使用して、任意のブロックを廃止(問題取り残されていない、任意のコード):
type
TDataManager = class
private
FAccessCount: integer;
FData: TDataClass;
public
procedure Read(out _Some: integer; out _Data: double);
procedure Write(_Some: integer; _Data: double);
end;
procedure TDataManager.Read(out _Some: integer; out _Data: double);
var
Data: TDAtaClass;
begin
InterlockedIncrement(FAccessCount);
try
// make sure we get both values from the same TDataClass instance
Data := FData;
// read the actual data
_Some := Data.Some;
_Data := Data.Data;
finally
InterlockedDecrement(FAccessCount);
end;
end;
procedure TDataManager.Write(_Some: integer; _Data: double);
var
NewData: TDataClass;
OldData: TDataClass;
ReaderCount: integer;
begin
NewData := TDataClass.Create(_Some, _Data);
InterlockedIncrement(FAccessCount);
OldData := TDataClass(InterlockedExchange(integer(FData), integer(NewData));
// now FData points to the new instance but there might still be
// readers that got the old one before we exchanged it.
ReaderCount := InterlockedDecrement(FAccessCount);
if ReaderCount = 0 then
// no active readers, so we can safely free the old instance
FreeAndNil(OldData)
else begin
/// here is the problem
end;
end;
残念ながら、OldDataインスタンスが置き換えられた後にそれを取り除くという小さな問題があります。他のスレッドが現在Readメソッド内にない場合(ReaderCount = 0)、それは安全に破棄できます。しかし、そうでない場合はどうすればいいですか? 私は次の呼び出しまでそれを保存してそこに置くことができますが、Windowsのスケジューリングは理論的には読み取りメソッド内にある間に読み取りスレッドをスリープさせることができ、まだOldDataへの参照を持っています。
上記のコードに他の問題がある場合は、教えてください。これは、複数のコアを持つコンピュータ上で実行され、上記の方法は非常に頻繁に呼び出されます。
この場合、Delphi 2007を内蔵メモリマネージャとともに使用しています。私は、新しいクラスを作成するときにメモリマネージャがおそらく何らかのロックを強制している可能性があることを認識していますが、私は今それを無視したいと思います。
編集:上記から明らかではないかもしれません:TDataManagerオブジェクトの全期間にわたって、書き込みアクセスのために競合する可能性のあるものではなく、データに書き込むスレッドは1つだけです。これはMREWの特別なケースです。
私は自己完結型のロックフリーコードには気をつけています。 TMREWSについて:典型的なマシンでユースケースのタイミングを立てる方法はありません。実装する方法はさまざまです。VCLでは、1つしかありません。異なる実装(タイミングを含む)を比較する記事については、http://www.codeproject.com/KB/threads/testing_rwlocks.aspxを参照してください。 – mghie