2012-06-25 1 views
4

Версия используется: Delphi 7.TStringList и TThread, что не освобождает все его памяти

Я работаю над программой, которая делает простой для петли на виртуальном ListView. Данные хранятся в следующей записи:

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

Пункт является индексом. SubItem1 статус операций (успех или нет). SubItem2 путь к файлу. Цикл для загружает каждый файл, выполняет несколько операций и затем сохраняет его. Операции выполняются в TStringList. Файлы размером около 2 мб каждый.

Теперь, если я выполняю операции на основной форме, он отлично работает.

Многопоточная проблема с огромной памятью. Так или иначе, TStringList, похоже, не полностью освобождается. После файлов 3-4k я получаю исключение EOutofMemory. Иногда программное обеспечение задерживается до 500-600 мб, иногда нет. В любом случае TStringList всегда возвращает исключение EOutofMemory, и файл больше не может быть загружен. На компьютерах с большей памятью требуется больше времени для получения исключения.

То же самое происходит с другими компонентами. Например, если я использую THTTPSend от Synapse, через некоторое время программное обеспечение не сможет создавать новые потоки, потому что потребление памяти слишком велико. Это около 500-600 мб, в то время как это должно быть, max, 100 МБ. На основной форме все работает нормально.

Я предполагаю, что ошибка на моей стороне. Возможно, я недостаточно разбираюсь в потоках. Я попытался освободить все на турнире Destroy. Я пробовал FreeAndNil процедура. Я пробовал только один поток за раз. Я попытался освободить нить вручную (нет FreeOnTerminate ...)

Не повезло.

Итак, вот код темы. Это только основная идея; а не полный код со всеми операциями. Если я удалю LoadFile prodecure, все будет хорошо. Поток создается для каждого файла в соответствии с пулом потоков.

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 потоков .

Спасибо за ваш вклад.

+0

@TLama Уже сделал это ... и в соответствии с FastMM, нет утечки памяти, за исключением тех из Delphi 7. – VanillaH

+0

@TLama 13 - 20 байт: AnsiString х 1 29 - 36 байт: Unknown х 1 45 - 52 bytes: TStringList x 2 Я считаю, что это утечки памяти из самого Delphi. Поправьте меня если я ошибаюсь. – VanillaH

+0

У меня никогда не было утечек памяти из Delphi. –

ответ

7

Есть (вероятно) никаких утечек в коде, который вы видите в вопросе.

Я говорю, вероятно, потому, что исключение, возникшее во время Execute, может привести к утечке. Время жизни списка строк должно быть защищено блоком finally.

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

Тем не менее, я ожидаю, что ласточка исключение LoadFile означает, что вы не утечка списка строк.

Вы говорите, что, возможно, созданы тысячи потоков. Каждый поток резервирует память для своего стека, а размер стека по умолчанию - 1 МБ. Когда у вас есть тысячи 1 МБ стеков, вы можете легко вывести или фрагментировать адресное пространство.

Я видел проблемы из-за кавалерного создания потоков в прошлом. Например, у меня была неудачная программа, когда она создавала и уничтожала потоки, причем не более 256 потоков. Это было на 16-ядерном компьютере с 4 ГБ адресного пространства. Возможно, у вас есть адресное пространство 2 ГБ.

Хотя вы заявляете, что в любой момент существует не более 50 потоков, я не уверен, как вы можете быть в этом уверены. Не в последнюю очередь потому, что вы установили FreeOnTerminate в True и тем самым сдали контроль за временем жизни ваших потоков.

Я предполагаю, что ваши проблемы связаны с количеством создаваемых вами потоков. Достаточно одного потока на процессор. Повторно используйте свои потоки. Очень дорого создавать и уничтожать поток для небольшой задачи.

Если этого недостаточно, чтобы решить свои проблемы, вам нужно будет показать код, управляющий потоком жизни.

И, наконец, интересно, какую выгоду вы извлечете из потоковой передачи этого приложения. Если он привязан к IO, то потоковая версия может быть медленнее!

+0

Я думал то же самое, но смотрю на обновление вопроса * только 50 потоков создаются и работают одновременно *. – TLama

+0

@TLama Что-то не складывается. Код в Q не протекает. Создание слишком большого количества потоков - это простой способ съесть все ваше виртуальное адресное пространство. –

+0

@DavidHeffernan - В каком режиме отказа эта программа пострадала? (Создание/уничтожение потоков, не более 256?). Сейчас я борюсь с подобной проблемой - системой реального времени, которая сэмплирует данные с некоторого оборудования около 10 раз/сек. Сторонний драйвер (по какой-либо причине) создает и уничтожает поток для каждого измерения (каждый занимает около 10 мс). Через день или около того непрерывное циклическое движение ~ 36000 нитей/час, оно болото до ползания. В противном случае программа будет жесткой и худой. Любопытно, каков был ваш опыт. –

1

На основании данной информации невозможно воспроизвести вашу ошибку. Некоторые подсказки сделаны Реми и Дэвидом, которые могут вам помочь.

Рассматривая структуру вашей программы, поток можно разделить на два классических решения.

Первая часть, где вы делегируете задачи различным потокам, является проблемой Single-Producer-Multiple-Consumer. Здесь его можно решить, создав небольшое количество потоков, передав им поточную очередь объектов. Основной поток затем толкает объекты задачи в очередь. Потребительские потоки заботятся о задачах проверки отдельных файлов.

Вторая часть, в которой результат должен быть перенесен в основной поток, является проблемой Multiple-Producer-Single-Consumer. Если вы передадите вторую очередь объектов с потоками в потоки при инициализации, они могут легко поместить результаты в очередь. Слейте очередь результатов из основного потока в событии таймера.