私のアプリケーションでは、テキスト ファイル (ログ、トレースなど) を書き込むときにTFileStream
クラスを使用します。マルチスレッド環境でデータを書き込む場合がありますが、その手順は次のとおりです。
1- キャッシュ データの書き込み
2- 1000 行ごとにファイルに保存します。
3- データを消去します。
このプロセスは、すべての処理中に繰り返されます。
問題の説明:
16 スレッドの場合、システムは次の例外をスローします。
アクセス違反 - ファイルは別のアプリケーションで既に使用されています。
別のスレッドを開く必要があるときに、あるスレッドで使用されているハンドルがまだ閉じられていないために、これが発生していると思います。
アーキテクチャを次のように変更しました: (以下は新しい実装です)
以前の方法では、TFileStream は FileName および Mode パラメータで作成され、ハンドルを閉じて破棄されました (私は TMyFileStream を使用していませんでした)。
TMyFileStream = class(TFileStream)
public
destructor Destroy; override;
end;
TLog = class(TStringList)
private
FFileHandle: Integer;
FirstTime: Boolean;
FName: String;
protected
procedure Flush;
constructor Create;
destructor Destroy;
end;
destructor TMyFileStream.Destroy;
begin
//Do Not Close the Handle, yet!
FHandle := -1;
inherited Destroy;
end;
procedure TLog.Flush;
var
StrBuf: PChar; LogFile: string;
F: TFileStream;
InternalHandle: Cardinal;
begin
if (Text <> '') then
begin
LogFile:= GetDir() + FName + '.txt';
ForceDirectories(ExtractFilePath(LogFile));
if FFileHandle < 0 then
begin
if FirstTime then
FirstTime := False;
if FileExists(LogFile) then
if not SysUtils.DeleteFile(LogFile) then
RaiseLastOSError;
InternalHandle := CreateFile(PChar(LogFile), GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ, nil, CREATE_NEW, 0,0);
if InternalHandle = INVALID_HANDLE_VALUE then
RaiseLastOSError
else if GetLastError = ERROR_ALREADY_EXISTS then
begin
InternalHandle := CreateFile(PChar(LogFile), GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ, nil, OPEN_EXISTING, 0,0);
if InternalHandle = INVALID_HANDLE_VALUE then
RaiseLastOSError
else
FFileHandle := InternalHandle;
end
else
FFileHandle := InternalHandle;
end;
F := TMyFileStream.Create(FFileHandle);
try
StrBuf := PChar(Text);
F.Position := F.Size;
F.Write(StrBuf^, StrLen(StrBuf));
finally
F.Free();
end;
Clear;
end;
end;
destructor TLog.Destroy;
begin
FUserList:= nil;
Flush;
if FFileHandle >= 0 then
CloseHandle(FFileHandle);
inherited;
end;
constructor TLog.Create;
begin
inherited;
FirstTime := True;
FFileHandle := -1;
end;
別のより良い方法はありますか?
この実装は正しいですか?
これを改善してもいいですか?
ハンドルについての私の推測は正しかったですか?
すべての広告が同じ Log オブジェクトを使用します。
再入場はありません、チェックしました!TFileStream に問題があります。
Add へのアクセスは同期、つまりクリティカル セッションを使用し、1000 行に達すると Flush プロシージャが呼び出されます。
PS: サードパーティ製のコンポーネントは必要ありません。自分で作成したいのです。