Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

ошибка в уничтожении потока Delphi+Thread+TerminateThread [D6, WinXP]


Страницы:   1 2 3

Delphi5.01 ©   (2006-05-31 06:13:00) 

Хотя в конкретном моем случае речь идет о Delphi, но думаю вопрос больше общий так как касается API функции, которые одинаковые как по синтаксису так и среде использованию для всех (почти) языков. Дело касается потоков и функции TerminateThread(thread.Handle, 0);

Теперь немного о моей программе. В программе есть потоки которые выполняют некую работу. Допустим качают данные с какого-то сайта. Назовем его "поток скачки". Так как у скачки должен быть timeout, я "поток скачки" поместил в другой поток который сделал за временем, и в случае его истечения заверещал "поток скачки" аварийно, TerminateThread("поток скачки".handle, 0); Назовем этот "поток проверки".

В итоге что бы выполнить задания закачки одновременно я создавал несколько "потоков проверки" и ждал пока они все закончат работу. Результат мог быть или положительный, а значит "поток скачки" завершил работу удачно, либо отрицательный, не смогли скачать или кончилось время. После чего данный "поток проверки" убивался также функцией TerminateThread("поток проверки".handle, 0); после чего заново запускался но уже с новыми параметрами.

Но при тестировании и наблюдениями за поведением кода, было замечено что после того как первые 10 "потоков проверки" (а это был массив) завершал работу и запускались новые 10 потоков некоторые вообще исчезали. Другими словами есть подозрение что функция TerminateThread убивала больше одного потока за раз! Так как при подсчете, а я учитывал когда поток заканчивал работу, разница была в 50%. Толи потоки не могли быть убиты, толи наоборот их убили случайно :-)

Одним словом, конечно сложно что-то советовать прочитав выше указанный алгоритм, но может у вас была похожая ситуация? И вообще как можно завершить работу потока аварийно. Что бы наглухо, так как алгоритм Thread.Teriminate не подходит, он завершает работу только после того как внутренние компоненты (созданные в потоке) завершат работу.

Заранее благодарю всех сначала за время потраченное на прочтение такого большого текста и потом за ответ :-)


Delphi5.01   (2006-06-02 04:56:00) [1]

Сегодняшние тестирования вообще удивили меня, программа после того как были запушены/уничтожены в суме около 300 поток вырубилась. Снова не показав ошибку. Даже не могу представить как можно отследить в такой ситуации ошибку и где она вообще возникает :-( Не говоря о том что не возможности следить за состоянием потоков. Думаю продеться вести файл лог, хотя не совсем представляю что за информацию туда писать :-(


Error_   (2006-06-02 04:19:00) [2]

Это думаю одно из тех вешей что мне точно поможет :-) TThreadList сейчас пошел читать доку по этому классу
Спасибо
п.с. Как на счет моего кода, есть баги?


Error_   (2006-06-02 03:22:00) [3]

Сори что долго не отвечал, был в out-е :-)
Вот код который я имею на данный момент, как можно догадаться он для проверки работаспособности прокси:

unit Kernel;

interface

uses
 Classes, IdHTTP, Windows, Functions, Dialogs;

type
 TProxyType = (ptUnknown, ptBad, ptTransparent, ptAnonymouse, ptElite, ptElitePlus);

 TProxyCheckerThread = class(TThread)
 private
   fIdHTTP: TIdHTTP;
   fUrl: String;
   fCheckString: String;
   fResult: Boolean;
   fResultString: String;
 protected
 public
   constructor Create(Url, Server: String; Port: Integer); overload;
   procedure Execute; override;
       
   procedure TerminateConnection();
   function GetResult: Boolean;
   function GetResultString: String;
 end;

 TProxyChecker = class(TThread)
 private
   fUrl: String;
   fCheckString: String;
   fServer: String;
   fPort: Integer;
   fTimeout: Integer;
   fResult: Boolean;

   //Result variables
   fProxyType: TProxyType;
   fProxyLatency: Integer;
   fProxySSLSupport: Boolean;
   fProxyCountryCode: String;
   fProxyCountryName: String;
 protected
    ...удалено для облегчения кода...
 public
   constructor Create(Url, CheckString, Server: String;
     Port, Timeout: Integer); overload;
   procedure Execute; override;

   function GetResult: Boolean;
   function GetProxyType: TProxyType;
   function GetLatancy: Integer;
 end;

implementation

//======================================================
//TProxyCheckerThread BEGIN
//======================================================

constructor TProxyCheckerThread.Create(Url, Server: String; Port: Integer);
begin
 //Create thread in suspending mode
 inherited Create(True);

 fResult := false;
 fResultString := '';
 //Create TIdHTTP component to retrive information
 fIdHTTP := TIdHTTP.Create(nil);
 fIdHTTP.Request.ProxyServer := Server;
 fIdHTTP.Request.ProxyPort := Port;
 fUrl := Url;
 Resume;
end;

procedure TProxyCheckerThread.Execute;
begin
 try
   fResultString := fIdHTTP.Get(fUrl);
 except
   //Error in connection
   exit;
 end;
 fResult := True;
end;

procedure TProxyCheckerThread.TerminateConnection;
begin
 fIdHTTP.DisconnectSocket;
 fIdHTTP.Free;
end;

...удалено для облегчения кода...

//======================================================
//TProxyCheckerThread END
//======================================================

//======================================================
//TProxyChecker BEGIN
//======================================================

constructor TProxyChecker.Create(Url, CheckString, Server: String;
 Port, Timeout: Integer);
begin
 //Create thread in suspending mode
 inherited Create(True);
 //Set thread's settings and execute it
 //Priority := tpIdle;
 fUrl := Url;
 fCheckString := CheckString;
 fServer := Server;
 fPort := Port;
 fTimeout := Timeout;
 //Prepare global variables
 fResult := false;
 fProxyType := ptBad;
 Resume;
end;

procedure TProxyChecker.Execute;
 var
   PCT: TProxyCheckerThread;
   StartTime: Integer;
   timePass: Integer;
begin
 //Prepare thread and execute to recieve result from URL
 PCT := TProxyCheckerThread.Create(fUrl, fServer, fPort);
 StartTime := GetTickCount;
 timePass := 0;
 //Work until time will finish and result from page will be recieved
 while (timePass < fTimeout) and (not PCT.GetResult) do begin
   timePass := GetTickCount-StartTime;
   //Wait for result or timeout
 end;
 //Checking if result exist and analyze it
 if PCT.GetResult then begin
   fProxyLatency := timePass;
   AnalyzeResult(PCT.GetResultString);
 end else
   fProxyType := ptBad;
 //Remove thread, even in case if it still running ;-)
 //First of all of cause terminate any connection
 PCT.TerminateConnection;
 PCT.Terminate;
 PCT.Free;
 fResult := True;
end;

...удалено для облегчения кода...

//======================================================
//TProxyChecker END
//======================================================
end.


DVM   (2006-06-01 16:52:00) [4]


> Думаю теперь все грамотно?

Лучше сделай так:

Thread.FreeOnTerminate := false;

Thread.fIdHTTP.DisconnectSocket;
Thread.Terminate;
Thread.WaitFor;
Thread.Free;
Thread := nil;

procedure TMyThread.Destroy;
begin
fIdHTTP.Free;
end;


Alex Konshin   (2006-06-01 14:40:00) [5]

Ты не прав с тем, что сразу после закрытия сокета выдаешь Free на нить.
Дай ей время на нормальное завершение. Просто через какое-то время проверяй WaitForSingleObject с нулевым таймаутом (если проверяющая нить занимается чем-то еще) или жди с таймаутом (если ей делать нечего).
Кстати, у тебя чему равно FreeOnTerminate для этих нитей.
Вопрос очень важный, т.к. если он True, то объект будет освобождаться дважды (если меры не принять), а если False, то тебе нужно следить за удалением самому. Собственно это самый сложный момент во всей этой кухне и это не просто сделать действительно надежно. Наверно для тебя самое простое будет посмотреть на класс TThreadList. Суть в том, чтобы всегда добавлять/удалять нити в/из спис-ок/ка, но не уничтожать, если нити там нет. Естественно, не забывать блокировать список при операциях с ним. Это может быть несколько утомительно, но зато более надежно.

Короче, как я и говорил грамотно все сделать непросто, потому все ленятся и потому мы видим приложения, которые виснут.


Рез_Тест_2   (2006-06-01 10:33:00) [6]


> После Terminate потока все таки лучше ждать завершения

И по этой причине и подвисла программа, так как поток так и не смог приглушить сокет, и поток как говориться подвис. Такое у меня было с I/O, когда считывал с поврежденной болванки, в таком случае надо ждать пока не сработает Timeout самого сокета :-( И сработает ли вообше? В моем случае просто прога вырубалась автоматом, без всяких ошибок.
С TerminateThread такого нету, все как и запланированно, но вот с освобождением ресурсов не все OK


Рез_Тест_2   (2006-06-01 10:30:00) [7]

Тестирование с функцией TerminateThread

Начну с самого лавного, утечка в памяти есть. Не все ресурсы высвобождаются, хотя зато все работает как часы. Потеря не велика до 1мб при запуске/уничтожении 1000 потоков. Сначала было больше пока я не добавил строку IdHTTP.Free до уничтожения потока.

Теперь возникает логический вопрос, как все же убить поток удачно, или что еще надо зачистить? Кроме IdHTTP поток не использует ничего кроме переменных. Не думаю что именно они и не освобождаются И как можно системе передать сообщение что типа убей поток и подчисти все, или дядя Бил это не предусмотрел? И по этой причине винда часто летит?


DVM   (2006-06-01 10:11:00) [8]


> Delphi5.01 ©   (01.06.06 06:10) [31]

После Terminate потока все таки лучше ждать завершения


Error_   (2006-06-01 10:06:00) [9]

Лог файлы показали что, после 500 запусков и уничтожений остаются потоки которые не могут быть завершены! А именно мое подозрение касающийся fIdHTTP.DisconnectSocket не срабатывает и поток продолжает работать. Сейчас думаю есть смысл проверить что будет с TerminateThread и запуском 500 раз? Если ошибок не будет то мое подозрение подтвердиться фактом


Error_   (2006-06-01 09:13:00) [10]

Как и можно было ожидать, где-то просочилась ошибка :-( Хоть поток удаляется по всем правилам, где-то генерируется ошибка и прога автоматом, без сообщений об ошибке вырубается Когда и при каких обстоятельствах пока не известно, но где-то после 200 запуска/уничтожения потока (прогу оставил работать, сам пошел чаек попить).
Сейчас добавлю в программу ведения логов и буду ловить ошибку. А как можно отловить все ошибки возникшие при работе программы? Что бы хотя бы понять где приблизительно ошибка


Delphi5.01   (2006-06-01 06:10:00) [11]

Думаю теперь все грамотно?

Thread.TerminateConnection;
Thread.Terminate;
Thread.Free;

procedure TMyThread.TerminateConnection;
begin
 fIdHTTP.DisconnectSocket;
 fIdHTTP.Free;
end;

Что скажете теперь знатоки? Проблем и ошибок не было, код работает как часики, если мне не показалось то он работает намного быстрей :-) Чем с конструкцией TerminateThread :-)


Delphi5.01   (2006-06-01 05:57:00) [12]

procedure DisconnectSocket; virtual;

Description

DisconnectSocket is a procedure used to close the socket binding for the connection. Unlike the Disconnect method, DisconnectSocket is not overridden by descendant classes to provide termination sequences required for a particular protocol.
DisconnectSocket is used when an error has occurred in a protocol handler and a guaranteed disconnect is needed.


Delphi5.01   (2006-06-01 05:55:00) [13]

Блин в D6 у копонетна IdHTTP нет Socket.Close :-( наверно вы говорите о D9? У меня однажды была похожая ситуация но с SSL и компонентом Telnet. Или я что-то не првильно понял?


Delphi5.01   (2006-06-01 03:14:00) [14]

Остается одно, делать как советуют и пробовать на большом количестве созданий и уничтожений потока, и главное в аварийной ситуации. При этом надо наблюдать за системными ресурсами.
Тестирование буду проводить сегодня, если несколько дней не увидите моего поста, значит загубил систему :-)


Eraser   (2006-05-31 18:49:00) [15]


> DVM ©   (31.05.06 17:57) [21]


> Исходников компонента нет

как это нет? ещё как есть.


DVM   (2006-05-31 18:36:00) [16]


> а чтобы не тормозил интерфейс при коннекте есть TIdAntifrreze

Так у него доп. поток вроде. И так не будет тормозить. Поток лучше, чем TIdAntifrreze, т.к. последний это лишь Application.ProcessMessages по таймеру.


DVM   (2006-05-31 18:31:00) [17]


> Хотя меня вот что интересует возвратить ли управления процедура/функция
> Socket.Close даже если компонент подвивший?

Если подвис на Winsock функциях, то возвратит.


Delphi5.01   (2006-05-31 18:18:00) [18]


> так у TIdHTTP есть свойство ConnectTimeout

Насколько точно данный таймаут работает верно? Есть ли исключения при которых таймаут не сработает. Например если повисание при connect-е?


> IdHTTP1.Socket.Close; ???

Думаю это то что надо. Я для страховки буду делать вот так.

буду устанавливать параметр
IdHTTP.ConnectTimeout

Потом по таймауту потока, если главный таимаут не сработал за "время + 2сек"
IdHTTP.Socket.Close;
IdHTTP.Free;
TerminateThreat();

Думаю после всего этого все пройдет супер, если возникнут траблы с прикрытием то прибьем все автоматом. Хотя меня вот что интересует возвратить ли управления процедура/функция Socket.Close даже если компонент подвивший?


umbra   (2006-05-31 18:09:00) [19]

а чтобы не тормозил интерфейс при коннекте есть TIdAntifrreze


DVM   (2006-05-31 18:00:00) [20]


> Delphi5.01 ©   (31.05.06 17:27) [19]

IdHTTP1.Socket.Close; ???


Страницы:   1 2 3

Наверх

Титульная страница Поиск, карта сайта Написать письмо