6

私のプログラムは着信文字列 (Telnet、HTTP などから) を処理しており、これらをログ用に Delphi XE2 でテキスト ファイルに書き込む必要があります。

プログラムがクラッシュする場合があり、残りの文字列が失われていないことを確認する必要があるため、着信文字列ごとにファイルを開いたり閉じたりすると、パフォーマンスの問題が発生します。たとえば、次のコードは完了するまでに 8 秒かかります。

私のコードは以下に含まれています。パフォーマンスを改善する方法はありますか?

(以下のテストではButton : Button1、 、OnClickイベント、および を含むフォームを作成するだけですLabel : lbl1)。

Procedure AddToFile(Source: string; FileName :String);
var
  FText : Text;
  TmpBuf: array[word] of byte;
Begin
  {$I-}
  AssignFile(FText, FileName);
  Append(FText);
  SetTextBuf(FText, TmpBuf);
  Writeln(FText, Source);
  CloseFile(FText);
  {$I+}
end;

procedure initF(FileName : string);
Var  FText : text;
begin
  {$I-}
  if FileExists(FileName) then  DeleteFile(FileName);
  AssignFile(FText, FileName);
  ReWrite(FText);
  CloseFile(FText);
  {$I+}
end;

procedure TForm1.Button1Click(Sender: TObject);
var tTime : TDateTime;
    iBcl : Integer;
    FileName : string;
begin
  FileName := 'c:\Test.txt';
  lbl1.Caption := 'Go->' + FileName; lbl1.Refresh;
  initF(FileName);
  tTime := Now;
  For iBcl := 0 to 2000 do
    AddToFile(IntToStr(ibcl) + '   ' +  'lkjlkjlkjlkjlkjlkjlkj' , FileName);
  lbl1.Caption  :=  FormatDateTime('sss:zzz',Now-tTime);
end;
4

4 に答える 4

15

自動的にバッファリングされる を使用しTStreamWriter、そのバッファを にTFileStream自動的にフラッシュすることを処理できます。また、必要に応じて既存のファイルに追加することを選択したり、Unicode サポート用の文字エンコーディングを設定したり、オーバーロードされたさまざまなCreateコンストラクターで別のバッファー サイズ (デフォルトは 1024 バイトまたは 1K) を設定したりできます。

( をフラッシュするとTStreamWriter、 の内容が に書き込まれるだけであることに注意してください。OS ファイル システム バッファはフラッシュされないため、が解放されるまで、ファイルは実際にはディスクに書き込まれません。)TStreamBufferTFileStreamTFileStream

毎回 StreamWriter を作成しないでください。一度作成して開き、最後に閉じます。

function InitLog(const FileName: string): TStreamWriter;
begin
  Result := TStreamWriter.Create(FileName, True);
  Result.AutoFlush := True;         // Flush automatically after write
  Result.NewLine := sLineBreak;     // Use system line breaks
end;

procedure CloseLog(const StreamWriter: TStreamWriter);
begin
  StreamWriter.Free;
end;

procedure TForm1.Button1Click(Sender: TObject);
var 
  tTime : TDateTime;
  iBcl : Integer;
  LogSW: TStreamWriter;
  FileName: TFileName;
begin
  FileName := 'c:\Test.txt';
  LogSW := InitLog(FileName);
  try
    lbl1.Caption := 'Go->' + FileName; 
    lbl1.Refresh;
    tTime := Now;

    For iBcl := 0 to 2000 do
      LogSW.WriteLine(IntToStr(ibcl) + '   ' +  'lkjlkjlkjlkjlkjlkjlkj');

    lbl1.Caption  :=  FormatDateTime('sss:zzz',Now - tTime);
  finally
    CloseLog(LogSW);
  end;
end;
于 2012-11-09T12:57:07.190 に答える
3

重要なデータをディスクに保存するためにファイルを再度開く代わりに、FlushFileBuffers関数を使用するか、および とフラグを指定してCreateFile関数を呼び出すことにより、バッファリングされていない I/O 用にファイルを開くことができます (最初のリンクのセクションを参照)。FILE_FLAG_NO_BUFFERINGFILE_FLAG_WRITE_THROUGHRemarks

于 2012-11-09T11:33:53.447 に答える
2

アプリケーションがクラッシュした場合にデータが失われないように、書き込みのたびにキャッシュをフラッシュする必要があることが問題のようです。

ここでの他の答えは優れていると確信していますが、コードにそのような大規模な変更を加える必要はありません。あなたがする必要があるのは Flush(FText) 、各書き込みの後に呼び出すことです。

const
  // 10 million tests
  NumberOfTests = 1000000;

  // Open and close with each write:        19.250 seconds

  // Open once, and flush after each write:  5.686 seconds

  // Open once, don't flush                  0.439 seconds

var
  FText : Text;
  TmpBuf: array[word] of byte;

procedure initF(FileName : string);
begin
  {$I-}
  if FileExists(FileName) then  DeleteFile(FileName);
  AssignFile(FText, FileName);
  ReWrite(FText);
  SetTextBuf(FText, TmpBuf);
  {$I+}
end;

procedure CloseTheFile;
begin
  CloseFile(FText);
end;

Procedure AddToFile(Source: string);
Begin
  {$I-}
  Writeln(FText, Source);

  // flush the cache after each write so that data will be written
  // even if program crashes.
  flush ( fText );              // <<<====   Flush the Cache after each write

  {$I+}
end;

procedure TForm1.Button1Click(Sender: TObject);
var tTime : TDateTime;
    iBcl : Integer;
    FileName : string;
begin
  FileName := 'c:\Test.txt';
  lbl1.Caption := 'Go->' + FileName; lbl1.Refresh;
  initF(FileName);

  // put file close in a try/finally block to ensure file is closed
  // even if an exception is raised.
  try

    tTime := Now;
    For iBcl := 0 to NumberOfTests-1 do
      AddToFile(IntToStr(ibcl) + '   ' +  'lkjlkjlkjlkjlkjlkjlkj');
    lbl1.Caption  :=  FormatDateTime('sss:zzz',Now-tTime);

  finally
    CloseTheFile;
  end;
end;
于 2012-11-10T12:18:58.027 に答える