1

常に実行され、静的配列を反復処理する Win32 スレッド (TThread なし) があります。メインスレッドは配列のフィールドを変更できます。Windows クリティカル セクション (TRTLCriticalSection) のみを使用して、TThreadList (no-vcl アプリケーションの場合) などのコンポーネントを使用せずに、このスレッド セーフを実現する最善の方法は何ですか?

コード:

type
  T = record
    Idx: Integer;
    Str: string;
    Num: Real;
    Enabled: Boolean;
  end;

var
  A: Array[0..9] of T;
  Cnt: Integer;
  CS: TRTLCriticalSection;

procedure thread;
var
  I: Integer;
begin
  while True do
  begin
    for I := Low(A) to High(A) do
    begin
      if A[I].Enabled then
      begin
        //modify some fields from A[I]

        Inc(A[I].Idx);
        if A[I].Idx >= 10 then
        begin
          A[I].Enabled := False;
          InterlockedDecrement(Cnt);
        end;
      end;
    end;
    if Cnt = 0 then Sleep(1);
  end;
end;

procedure Add(...); //called only from mainthread

  function GetFreeField: Integer;
  begin
    for Result := Low(A) to High(A) do
      if not A[Result].Enabled then Exit;
    Result := -1;
  end;

var
  I: Integer;
begin
  I := GetFreeField;
  if I = -1 then Exit;

  //set fields A[I]

  A[I].Enabled := True;
  InterlockedIncrement(Cnt);
end;

最初に、配列は enabled = false および cnt = 0 で初期化されます。

次の変更で十分ですか?

procedure thread;
var
  I: Integer;
begin
  while True do
  begin
    for I := Low(A) to High(A) do
    begin
      EnterCriticalSection(CS);
      if A[I].Enabled then
      begin
        LeaveCriticalSection(CS);
        //modify some fields from A[I]

        Inc(A[I].Idx);
        if A[I].Idx >= 10 then
        begin
          EnterCriticalSection(CS);
          A[I].Enabled := False;
          LeaveCriticalSection(CS);

          InterlockedDecrement(Cnt);
        end;
      end
      else
        LeaveCriticalSection(CS);
    end;
    if Cnt = 0 then Sleep(1);
  end;
end;

procedure Add(...); //called only from mainthread
var
  I: Integer;
begin
  I := GetFreeField;
  if I = -1 then Exit;

  //set fields A[I]

  EnterCriticalSection(CS);
  A[I].Enabled := True;
  LeaveCriticalSection(CS);

  InterlockedIncrement(Cnt);
end;
4

1 に答える 1

1

あなたのデザインは次のように見えます:

  1. メインスレッドは、Enabledフラグを からFalseにのみ切り替えますTrue
  2. ワーカー スレッドは、フラグを逆方向にのみ切り替えます。
  3. ここに表示されている以外のコードは、配列にアクセスしません。

そうであれば、クリティカル セクションのない元のコードは、既にスレッド セーフになっています。少なくとも、強力なメモリ モデルを使用するハードウェア上ではそうです。たとえば、Intel x86 または x64 アーキテクチャ。ブール値はEnabled、スレッド間の同期バリアとして機能します。

しかし、あなたのデザイン全体に欠陥があるように見えます。while Trueループとは、Sleep私にいくつかのアラームを引き起こします。そのスレッドは正当な理由もなく繰り返し実行されます。メインスレッドが配列に変更を加えた場合にのみ、スレッドでコードを実行する必要があります。スレッドを起動するには、シグナル (Windows イベントなど) を使用することをお勧めします。

于 2012-10-10T12:02:22.753 に答える