4

使用したバージョン: Delphi 7。

仮想 ListView で単純なforループを実行するプログラムに取り組んでいます。データは次のレコードに保存されます。

type TList=record
  Item:Integer;
  SubItem1:String;
  SubItem2:String;
end;

項目は索引です。SubItem1操作のステータス (成功または失敗)。SubItem2ファイルへのパス。forループは各ファイルをロードし、いくつかの操作を行ってから保存します。操作は TStringList で行われます。ファイルはそれぞれ約2MBです。

ここで、メイン フォームで操作を行うと、完全に機能します。

マルチスレッド、巨大なメモリの問題があります。どういうわけか、TStringList は完全に解放されていないようです。3 ~ 4k ファイルの後、EOutofMemory 例外が発生します。ソフトウェアが 500 ~ 600 MB に固執する場合もあれば、そうでない場合もあります。いずれにせよ、TStringList は常に EOutofMemory 例外を返し、ファイルをロードできなくなります。より多くのメモリを搭載したコンピューターでは、例外を取得するのに時間がかかります。

他のコンポーネントでも同じことが起こります。たとえば、 Synapse からTHTTPSendを使用すると、しばらくすると、メモリ消費量が多すぎるため、ソフトウェアは新しいスレッドを作成できなくなります。最大100MBである必要がありますが、約500〜600MBです。メイン フォームでは、すべて正常に動作します。

間違いは私の側にあると思います。多分私はスレッドを十分に理解していません。Destroyイベントですべてを解放しようとしました。FreeAndNil手順を試しました。一度に 1 つのスレッドだけで試しました。スレッドを手動で解放しようとしました(FreeOnTerminateはありません...)

運がない。

これがスレッドコードです。これはあくまでも基本的な考え方です。すべての操作を含む完全なコードではありません。LoadFile プロシージャを削除すると、すべて正常に動作します。スレッドプールに従って、ファイルごとにスレッドが作成されます。

unit OperationsFiles;

interface

uses Classes, SysUtils, Windows;

type
 TOperationFile = class(TThread)
 private
  Position : Integer;
  TPath, StatusMessage: String;
  FileStringList: TStringList;
  procedure UpdateStatus;
  procedure LoadFile;
 protected
  procedure Execute; override;
 public
  constructor Create(Path: String; LNumber: Integer);
 end;

implementation

uses Form1;

procedure TOperationFile.LoadFile;
begin
 try
  FileStringList.LoadFromFile(TPath);
  // Operations...
  StatusMessage := 'Success';
 except
  on E : Exception do StatusMessage := E.ClassName;
 end;
end;

constructor TOperationFile.Create(Path : String; LNumber: Integer);
begin
 inherited Create(False);
 TPath := Path;
 Position := LNumber;
 FreeOnTerminate := True;
end;

procedure TOperationFile.UpdateStatus;
begin
 FileList[Position].SubItem1 := StatusMessage;
 Form1.ListView4.UpdateItems(Position,Position);
end;

procedure TOperationFile.Execute;
begin
 FileStringList:= TStringList.Create;
 LoadFile;

 Synchronize(UpdateStatus);

 FileStringList.Free;
end;

end.

何が問題なのですか?

ある時点で、作成されるスレッドが多すぎるのではないかと考えました。ユーザーが 100 万個のファイルをロードすると、最終的には 100 万個のスレッドが作成されますが、同時に作成されて実行されるスレッドは50 個だけです。

ご意見ありがとうございます。

4

2 に答える 2

7

質問で示したコードには (おそらく) リークはありません。

おそらく、途中で例外Executeが発生するとリークが発生する可能性があるためです。文字列リストの有効期間はfinallyブロックで保護する必要があります。

FileStringList:= TStringList.Create;
try
  LoadFile;
  Synchronize(UpdateStatus);
finally
  FileStringList.Free;
end;

そうは言っても、例外が飲み込まれるLoadFileことは、文字列リストを漏らさないことを意味すると思います。

おそらく何千ものスレッドが作成されるとおっしゃいました。各スレッドはスタック用にメモリを予約します。デフォルトのスタック サイズは 1MB です。何千もの 1MB スタックを予約すると、アドレス空間を簡単に使い果たしたり、断片化したりする可能性があります。

過去に無頓着なスレッドの作成による問題を見てきました。たとえば、存在するスレッドが 256 を超えないように、スレッドを作成および破棄するときに失敗するプログラムがありました。これは、4GB のアドレス空間を持つ 16 コアのマシン上にありました。おそらく 2GB のアドレス空間が利用可能です。

ある時点で存在するスレッドは 50 以下であると述べていますが、それをどのように確認できるかわかりません。特に、スレッドの有効期間を設定FreeOnTerminateし、それによって制御を放棄したためです。True

あなたの問題は、作成するスレッドの数に関連していると思います。プロセッサごとに 1 つのスレッドで十分です。スレッドを再利用します。小さなタスクのスレッドを作成して破棄するにはコストがかかります。

これで問題を解決できない場合は、スレッドの有効期間を管理するコードを示す必要があります。

最後に、このアプリをスレッド化することでどれだけのメリットが得られるのだろうか。IO バウンドの場合、スレッド バージョンの方が遅くなる可能性があります。

于 2012-06-25T06:23:33.890 に答える
1

提供された情報に基づいて、エラーを再現することはできません。Remy と David によって作成されたいくつかのヒントが役立つかもしれません。

プログラムの構造を見ると、フローは 2 つの古典的なソリューションに分けることができます。

タスクを別のスレッドに委譲する最初の部分がSingle-Producer-Multiple-Consumer問題です。ここでは、少数のスレッドを作成し、それらにスレッドセーフなオブジェクト キューを渡すことで解決できます。次に、メイン スレッドがタスク オブジェクトをキューにプッシュします。コンシューマ スレッドは、個々のファイル チェック タスクを処理します。

結果がメイン スレッドに転送される 2 番目の部分がMultiple-Producer-Single-Consumer問題です。初期化時に 2 番目のスレッド セーフ オブジェクト キューをスレッドに渡すと、スレッドは簡単に結果をキューに入れることができます。タイマー イベント内でメイン スレッドから結果キューを排出します。

于 2012-06-25T20:48:10.117 に答える