Используемая версия: Delphi 7.
Я работаю над программой, которая выполняет простой цикл for в Virtual ListView. Данные хранятся в следующей записи:
type TList=record
Item:Integer;
SubItem1:String;
SubItem2:String;
end;
Элемент — индекс. SubItem1 статус операций (успешно или нет). SubItem2 — путь к файлу. Цикл for загружает каждый файл, выполняет несколько операций, а затем сохраняет его. Операции происходят в TStringList. Файлы около 2мб каждый.
Теперь, если я выполняю операции в основной форме, все работает отлично.
Многопоточность, есть огромная проблема с памятью. Почему-то TStringList не кажется полностью освобожденным. После 3-4k файлов я получаю исключение EOutofMemory. Иногда софт зависает на 500-600мб, иногда нет. В любом случае TStringList всегда возвращает исключение EOutofMemory, и файл больше не может быть загружен. На компьютерах с большим объемом памяти получение исключения занимает больше времени.
То же самое происходит и с другими компонентами. Например, если я использую THTTPSend из Synapse, через некоторое время программа не сможет создавать новые потоки, потому что потребление памяти слишком велико. Это около 500-600 МБ, а должно быть максимум 100 МБ. На главной форме все работает нормально.
Думаю, ошибка на моей стороне. Возможно, я недостаточно понимаю темы. Я попытался освободить все в событии Destroy. Я попробовал процедуру FreeAndNil. Я пробовал только с одним потоком за раз. Я попытался освободить поток вручную (без 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.
В чем может быть проблема?
В какой-то момент я подумал, что, может быть, создается слишком много потоков. Если пользователь загружает 1 миллион файлов, в конечном итоге будет создан 1 миллион потоков, хотя одновременно создаются и выполняются только 50 потоков.
Спасибо за ваш вклад.
FileList
. Я подозреваю, что код вашего потока генерирует исключение, которое вы не обрабатываете, в обход вашего вызоваTStringList.Free()
. Либо добавьте блокtry/finally
, либо переопределитеDoTerminate()
, чтобыFree()
всегда вызывался. - person Remy Lebeau   schedule 25.06.2012TOperationFile.LoadFile
при проглатывании исключений фактически означает, что он не приведет к утечке списка строк. - person David Heffernan   schedule 25.06.2012Synchronize()
в основном потоке возникает исключение,Synchronize()
перехватывает и повторно вызывает его в контексте потока, вызвавшегоSynchronize()
. Таким образом,LoadFile()
может перехватывать исключения, аUpdateStatus()
— нет, что может привести к тому, что исключение будет передано обратно вExecute()
и пропустит вызовFileStringList.Free()
. - person Remy Lebeau   schedule 26.06.2012