2

アプリケーションの説明:

ユーザーがスレッドを介して複数の同時クエリを実行できるようにするアプリケーションがあります(一度に最大100)。

エラーのログ記録に使用するクラスがあります。アプリケーションでエラーが発生した場合は、クラスのインスタンスを作成し、プロシージャを呼び出してエラーをログファイルに書き込みます。

質問:

エラーロギングコードスレッドを安全にする必要があります。多くのスレッドが同時に実行され、同じエラーを生成している場合(たとえば、データベースに接続できない場合)、I / Oエラー32が発生していることに気付きました(アプリケーションが次のファイルに書き込もうとしたことが原因です)。すでに開いています)。

迅速で汚い修正として、ファイルに書き込むコードを試してみました...繰り返しループ内のブロックを除きます。例外がある場合(たとえば、ファイルがクラスの別のインスタンスによってすでに開かれている場合、別のスレッドによって開始されている場合)、フラグが「false」に設定されます。次のように、フラグが「true」になるまで(つまり、ファイルへの書き込みエラーが発生しなくなるまで)、ループは実行を続けます。

procedure TErrorLogging.logError(error: string);
var
     f: textfile;
     ok: boolean;
begin
     repeat
          ok := true;
          try
               assignfile(f, fLogFilename);
               if fileExists(fLogFilename) then append(f) else rewrite(f);
               writeln(f, error);
               closefile(f);
          except
               ok := false;
          end;
     until ok;
end;

コードのブロックを保護する正しい方法はクリティカルセクションを使用することですが、ロギングクラスを使用するさまざまなスレッドがいくつかあり、各インスタンスがあることを考えると、それをどのように実装するかはわかりません。スレッドには、ファイルへの書き込みに使用するロギングクラスの独自のインスタンスがあります(したがって、すべてが同じコードブロックに対して同期しているだけではありません)。

私が見ることができるように、オプション:

  1. 上記のコードを使用してください。このコードをそのままにしておくことに問題はありますか?これは迅速で汚い修正ですが、機能します。
  2. グローバルTCriticalSectionを使用します(どのように?)。
  3. スレッドが同期するロギングクラスのインスタンスを作成する単一のプロシージャをどこかで使用します(これにより、ロギングクラスを持つというオブジェクトが無効になると思います)。
4

2 に答える 2

6

ログエントリを追加するたびにログクラスのインスタンスを作成することは間違っているだけでなく、ログファイルを何度も開いたり閉じたりします。私は個人的に、文字列リストを内部的に使用し、基本的なメソッドがスレッドセーフであるクラスの1つのインスタンスを使用します。このようなもの:

type
  TErrorLog = class
  private
    FList: TStringList;
    FLock: TRTLCriticalSection;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Clear;
    procedure Add(const ErrorText: string);
    procedure SaveToFile(const FileName: string);
  end;

implementation

{ TErrorLog }

constructor TErrorLog.Create;
begin
  inherited Create;
  InitializeCriticalSection(FLock);
  FList := TStringList.Create;
end;

destructor TErrorLog.Destroy;
begin
  EnterCriticalSection(FLock);
  try
    FList.Free;
    inherited Destroy;
  finally
    LeaveCriticalSection(FLock);
    DeleteCriticalSection(FLock);
  end;
end;

procedure TErrorLog.Clear;
begin
  EnterCriticalSection(FLock);
  try
    FList.Clear;
  finally
    LeaveCriticalSection(FLock);
  end;
end;

procedure TErrorLog.Add(const ErrorText: string);
begin
  EnterCriticalSection(FLock);
  try
    FList.Add(ErrorText);
  finally
    LeaveCriticalSection(FLock);
  end;
end;

procedure TErrorLog.SaveToFile(const FileName: string);
begin
  EnterCriticalSection(FLock);
  try
    FList.SaveToFile(FileName);
  finally
    LeaveCriticalSection(FLock);
  end;
end;
于 2013-03-26T18:09:30.553 に答える
3

Delphiを知らない場合、一般的な設計ルールとして(可能な場合)、logError関数をスレッドセーフなArray、ArrayList、Queueオブジェクトなどに挿入し、バックグラウンドでファイルに書き込みます。おそらく5〜10秒ごとかそこら。これは、i / oの問題を処理するだけでなく、デバッグなどのために他のイベントをログに記録する場合に備えて、1秒あたり数千の書き込みに拡張する必要があります。

于 2013-03-26T18:01:54.013 に答える