1

私はいくつかのマルチスレッド アプリケーションを使用してきましたが、その一部にはスレッド保護オブジェクトが必要です。次の方法を使用して、個々のオブジェクト スレッドの保護を停止しています。

type
  TMyClass = class(TObject)
  private
    FLock: TRTLCriticalSection;
    FSomeString: String;
    procedure Lock;
    procedure Unlock;
    function GetSomeString: String;
    procedure SetSomeString(Value: String);
  public
    constructor Create;
    destructor Destroy; override;
    property SomeString: String read GetSomeString write SetSomeString;
  end;

implementation

constructor TMyClass.Create;
begin
  InitializeCriticalSection(FLock);
  Lock;
  try
    //Initialize some stuff
  finally
    Unlock;
  end;
end;

destructor TMyClass.Destroy;
begin
  Lock;
  try
    //Finalize some stuff
  finally
    Unlock;
  end;
  DeleteCriticalSection(FLock);
  inherited Destroy;
end;

procedure TMyClass.Lock;
begin
  EnterCriticalSection(FLock);
end;

procedure TMyClass.Unlock;
begin
  LeaveCriticalSection(FLock);
end;

function TMyClass.GetSomeString: String;
begin
  Result:= '';
  Lock;
  try
    Result:= FSomeString;
  finally
    Unlock;
  end;
end;

procedure TMyClass.SetSomeString(Value: String);
begin
  Lock;
  try
    FSomeString:= Value;
  finally
    Unlock;
  end;
end;

しかし、オブジェクトのリストを実装すると、各オブジェクトを安全に保護する方法がわかりません。次のようにオブジェクト リストを作成します。

type
  TMyClass = class;
  TMyClasses = class;

  TMyClass = class(TObject)
  private
    FOwner: TMyClasses;
  public
    constructor Create(AOwner: TMyClasses);
    destructor Destroy; override;
  end;

  TMyClasses = class(TObject)
  private
    FItems: TList;
    function GetMyItem(Index: Integer): TMyItem;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Clear;
    function Count: Integer;
    property Items[Index: Integer]: TMyClass read GetMyItem; default;
  end;

implementation

{ TMyClass }

constructor TMyClass.Create(AOwner: TMyClasses);
begin
  FOwner:= AOwner;
  FOwner.FItems.Add(Self);
  //Initialize some stuff...
end;

destructor TMyClass.Destroy;
begin
  //Uninitialize some stuff...
  inherited Destroy;
end;

{ TMyClasses }

constructor TMyClasses.Create;
begin
  FItems:= TList.Create;
end;

destructor TMyClasses.Free;
begin
  Clear;
  FItems.Free;
  inherited Destroy;
end;

procedure TMyClasses.Clear;
begin
  while FItems.Count > 0 do begin
    TMyClass(FItems[0]).Free;
    FItems.Delete(0);
  end;
end;

function TMyClasses.Count: Integer;
begin
  Result:= FItems.Count;
end;

function TMyClasses.GetMyItem(Index: Integer): TMyClass;
begin
  Result:= TMyClass(FItems[Index]);
end;

これには 2 つの方法があり、どちらの方法も信頼できません。1 つの方法は、リスト オブジェクト ( ) にクリティカル セクション ロックを実装し、その中の各オブジェクトがこのロックを共有することです ( andTMyClassesを呼び出すことによって) 。しかし、2 つの異なるスレッドは、このリストの 2 つの異なるオブジェクトを2 番目の方法は、それぞれの個別のオブジェクトに別のクリティカル セクションを配置することですが、これらが多すぎるのも危険ですよね? どうすればリストとすべてのオブジェクトを保護できますか?一緒にリスト?FOwner.Lock;FOwner.Unlock;

4

1 に答える 1

1

単一のオブジェクトへのアクセスをシリアル化する単純なクラスで使用するのと同じアプローチをリストクラスで使用することを現実的に期待することはできません。

たとえば、リストクラスには、以前の多くのように、Countプロパティとインデックス付きItems[]プロパティがあります。あなたのスレッドモデルはリストを変更できると思います。ここで、次のようなコードを記述するとします。

for i := 0 to List.Count-1 do
  List[i].Frob;

このループの実行中に別のスレッドがリストを変更するとします。まあ、それは明らかにランタイムの失敗につながるでしょう。したがって、上記のループはロックでラップする必要があると結論付けることができます。つまり、リストのスレッドセーフの側面を外部に公開する必要があります。現在の設計では、すべてを内部に保持することはできません。

クラスの内部でロックを保持したい場合は、プロパティCountItems[]プロパティを削除する必要があります。リストを次のようにすることができます(一部の部分が削除されています)。

type
  TThreadsafeList<T> = class
  private
    FList: TList<T>;
    procedure Lock;
    procedure Unlock
  public
    procedure Walk(const Visit: TProc<T>);
  end;
....
procedure TThreadsafeList<T>.Walk(const Visit: TProc<T>);
var
  Item: T;
begin
  Lock;
  try
    for Item in FList do
      Visit(Item);
  finally
    Unlock;
  end;
end;

そして今、あなたは上記のループをこれで置き換えることができます:

ThreadsafeList.Walk(
  procedure(Item: TMyItemClass)
  begin
    Item.Frob;
  end
);

手順Walkによって決定されるように、メソッドが特定のアイテムの削除をサポートできるように、この概念を拡張することは難しくありません。Visit

しかし、あなたが言うように、そのようなリストであなたができることはかなり無意味です。共有データはマルチスレッドの悩みの種です。各スレッドに必要なすべてのデータの独自のプライベートコピーを提供する問題を解決する方法を見つけることをお勧めします。その時点で同期は必要なく、すべて問題ありません。

最後にもう1つ。スレッドセーフの単一の概念はありません。スレッドセーフの意味は、コンテキストによって異なります。Eric Lippertはそれを最もよく言いました:あなたが「スレッドセーフ」と呼ぶこのことは何ですか?したがって、このような質問をするときはいつでも、特定のユースケースとスレッドモデルについて十分な詳細を提供する必要があります。

于 2012-11-26T21:40:35.483 に答える