Ru-Board.club
← Вернуться в раздел «Прикладное программирование»

» ошибка в уничтожении потока Delphi+Thread+TerminateThread

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

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

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

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

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

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

Добавлено:
Сори но кажеться я сам лично нашел ошибку, досканально проверял каждый компонент по отдельности и кажеться ошибка вообше банальная (в чужом компоненте не доработка )
Автор: FireZone
Дата сообщения: 31.05.2006 10:04
Delphi6
Будь очень внимательным к освобождению ресурсов при использовании TerminateThread. Например, если у тебя Thread.FreeOnTerminate установлено в истину, то автоматического уничтожения объекта Thread у тебя не произойдет при использовании TerminateThread. Метод Thread.Terminate предназначен для установки в истину поля Thread.Terminated, и в пользовательском методе Thread.Execute полезно проверять это поле (если в потоке используется цикл) и покидать цикл, чтобы метод Thread.Execute нормально завершился. Кроме того, если ты используешь в потоке блоки try-finally-end, то нет никакой гарантии, что блок finally у тебя выполнится при прерывании потока через TerminateThread, и, например, выделенная тобой в потоке память останется потерянной.
Автор: Anatoliy_A
Дата сообщения: 31.05.2006 10:26
>> И вообще как можно завершить работу потока аварийно.

Thread.Suspend;
Thread.Free;
Автор: dyr farot
Дата сообщения: 31.05.2006 11:26
все ресурсы в рабочем потоке выделяй только как члены класса -- тогда при уничтожении объекта они не потеряются.
к тому же непонятно зачем каждому рабочемы потоку выделять по потоку-надсмоторщику. достаточно и одного потока на всех
Автор: OdesitVadim
Дата сообщения: 31.05.2006 11:43
Anatoliy_A

Цитата:
>> И вообще как можно завершить работу потока аварийно.

Thread.Suspend;
Thread.Free;

Но стрёмно, поток может не завершиться так как надо.
Лучше сделать, так как сделали в одном сетевом сканере - создают ещё одно маленькое приложение и запускают несколько копий. Все проблемы быстро решаются
Автор: Mickey_from_nsk
Дата сообщения: 31.05.2006 12:56
Delphi6
Вообще, в умных книгах (например, в Рихтере) пишут, что такая методика (TerminateThread) не есть очень хорошо.
Может пересмотреть механизм скачки? Например, в самом потоке сделать таймаут, или по таймауту закрывать сокет? Это будет (я так думаю) более удачным решением. В этом случае сам поток поймет, что надо закрываться и грамотно освободит свои ресурсы.
Автор: Delphi6
Дата сообщения: 31.05.2006 16:13

Цитата:
Thread.Suspend;
Thread.Free;

это и есть то что я говорю нереально. Допустим у меня была в прошлом такая ситуация когда информация считывалась с носителя CD. Так вот если место где происходило считывание было повреждено, то даже по истечении времени команда suspend не работала, поток уничтожался только после того как истекало время считывания данных (системный таимаут) и генерировалась ошибка (ее конечно отлавливал), И что я имел в результате, 30 секунд ждал пока поток завершит работу.
Когда функция TerminateThread убивает поток насмерть мгновенно! Вот только не знаю насколько качественно освобождается потом память при вызове команды thread.free, хотя думаю память должна освобождаться Думаю это легко проверить если запустить и уничтожить до 10.000 потоков.


Цитата:
Лучше сделать, так как сделали в одном сетевом сканере - создают ещё одно маленькое приложение и запускают несколько копий. Все проблемы быстро решаются

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


Цитата:
все ресурсы в рабочем потоке выделяй только как члены класса

вот именно, у меня "поток закачки" создается в "потоке проверки" таким образом если удаляется главный поток то и удаляется все в нем, хотя для доп. безопасности можно их оформить как члены класса. ("поток закачки" член "потоке проверки" класса)


Цитата:
то автоматического уничтожения объекта Thread у тебя не произойдет

для этой цели я на всякий случай после это команды вызываю thread.free что как я думаю должно освобождать память.


Цитата:
Рихтере

Это есть зло, он даже писал что надо делать синхронизацию а это оказалось самой большой ошибкой, читал на этот счет много статей в интернете Так что кто знает


Цитата:
достаточно и одного потока на всех

ок если вы внимательно прочтете алгоритм то заметите что есть вероятность что код закачки в "потоке закачки" может повиснуть, а именно закачка может длиться очень долго если скорость соединения очень мала, а как вы собираетесь после таимаута его убить? Только запустив в потоке и контролируя пройденное время снаружи.
Зачем второй поток? А как вы собираетесь контролировать сразу несколько запушенных закачек? Не легче все это оформить в виде одного потока который сам будет следить за всем и через таимаут время выдаст результат или положительный или отрицательный. Сам проанализирует результаты работы "потока закачки". Вам же остается просто запустить 10 (к слову) "потоков проверки" и ждать когда же они все закончат работу (а они сто пудово закончат работу вовремя).

Ну думаю один очень мудрый совет получил на счет того что "поток закачки" лучше оформить в виде члена класса "поток проверки".

И что TerminateThread есть зло. Я согласен, даже читал один мануал в котором говорилось не о TermiateThread а что-то вроде SuspendThread который точно зло, так как в примере было показано как программу можно подвесить такой функцией случайно.

Вопрос до сих пор актуален, как вы убиваете поток, в котором не просто бесконечный цикл (на него команда suspend) сработает, а компонент который работает с внешним миром, типа считывает данные с носителей, качает данные из инета, или проверяет содержимое другой программы или еще что.

Добавлено:

Цитата:
Может пересмотреть механизм скачки? Например, в самом потоке сделать таймаут, или по таймауту закрывать сокет? Это будет (я так думаю) более удачным решением. В этом случае сам поток поймет, что надо закрываться и грамотно освободит свои ресурсы.

А как можно остановить закачку, допустим компонента TIdHTTP? Я думаю это не реально, хотя возможно есть способ, сейчас пробью в инете...

Добавлено:
Вот в соседнем форуме ведеться обсуждение и вот что один сказал

Цитата:
При работе с сокетами (сетью), к сожалению, нормального способа быстро уничтожить поток нет. Если сокет ждет подключения (ответа сервера), сервер не отвечает, поток можно будет убить только после возникновения таймаута. Многие известные программы (в том числе и от MS) уничтожают потоки аварийно, а потом как-то подчищают ресурсы.

http://delphimaster.ru/cgi-bin/forum.pl?id=1149041613&n=5
Автор: OdesitVadim
Дата сообщения: 31.05.2006 16:36
Delphi6

Цитата:
то автоматического уничтожения объекта Thread у тебя не произойдет     

для этой цели я на всякий случай после это команды вызываю thread.free что как я думаю должно освобождать память.

Будут подчищены только VCL ресурсы. А ведь ещё есть и Windows'овские. И неизвесно, как поведёт себя класс TThread при попытке удалить ресурсы, наличие которых под вопросом (поток мы прибили, а ресурсы потока ушли в свободное плаванье)
Автор: Delphi6
Дата сообщения: 31.05.2006 16:42
OdesitVadim
Ок, тепер ясно куда капать надо. А как можно подчистить все. Получаеться если я пользуюсь IdHTTP то надо перед уничтожением потока сделать IdHTTP.Free?
Автор: vserd
Дата сообщения: 31.05.2006 17:19
Delphi6

Цитата:
Когда функция TerminateThread убивает поток насмерть мгновенно! Вот только не знаю насколько качественно освобождается потом память при вызове команды thread.free,

Такой текст из справки по WIN32 API устроит?

TerminateThread is a dangerous function that should only be used in the most extreme cases. You should call TerminateThread only if you know exactly what the target thread is doing, and you control all of the code that the target thread could possibly be running at the time of the termination. For example, TerminateThread can result in the following problems:

·    If the target thread owns a critical section, the critical section will not be released.
·    If the target thread is executing certain kernel32 calls when it is terminated, the kernel32 state for the thread's process could be inconsistent.
·    If the target thread is manipulating the global state of a shared DLL, the state of the DLL could be destroyed, affecting other users of the DLL.



A thread cannot protect itself against TerminateThread, other than by controlling access to its handles. The thread handle returned by the CreateThread and CreateProcess functions has THREAD_TERMINATE access, so any caller holding one of these handles can terminate your thread.
If the target thread is the last thread of a process when this function is called, the thread's process is also terminated.
The state of the thread object becomes signaled, releasing any other threads that had been waiting for the thread to terminate. The thread's termination status changes from STILL_ACTIVE to the value of the dwExitCode parameter.

Terminating a thread does not necessarily remove the thread object from the system. A thread object is deleted when the last thread handle is closed.
Автор: OdesitVadim
Дата сообщения: 31.05.2006 18:31
Delphi6

Цитата:
Ок, тепер ясно куда капать надо. А как можно подчистить все. Получаеться если я пользуюсь IdHTTP то надо перед уничтожением потока сделать IdHTTP.Free?

Даже не знаю. А если поток в это время работает с компонентом? Не будет ли аксес виолейшенов? Поток обратиться к несуществующему компоненту. К тому же синхронизация необходима при доступе к компоненту. А там может быть проблема с критическими секциями. Поток вошёл в неё и не вышел. Основной поток не может обратиться к нему.
Я не знаю, как у Вас там организовано, но думаю, что с удалением будут бяки. И лучшим на мой взгляд есть создание дополнительной программы, которой оттдаются указания.
Автор: FireZone
Дата сообщения: 31.05.2006 18:43
Delphi6
Мне тут идейка пришла... Из контролирующего потока попробуй сделать closesocket своему IdHTTP, тогда процесс соединения отвалится и поток нормально завершится. Для этого, видимо, при создании экземпляра IdHTTP тебе нужно "положить куда-нибудь на видное место" хэндл лежащего в его основе сокета.
Автор: Delphi6
Дата сообщения: 01.06.2006 01:18
OdesitVadim
Что вы подразумеваете когда говорите что поток может обращаться к не существующему компоненту? Единственный компонент с которым он работает, создается в нем же самом. Получается если поток умирает то и компонент сдыхает. Или я не совсем правильно представляю что есть поток?

FireZone
Самая мудрая идея, вот это я и хочу проверить, на другом форуме говорили что именно так можно прикрыть соединение аварийно. Посмотрим как на практике.

vserd
Спасибо за выписку из доки.

Все больше и больше я начинаю понимать что есть вероятность потери/утечки ресурсов.

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

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

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

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

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

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

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

Теперь возникает логический вопрос, как все же убить поток удачно, или что еще надо зачистить? Кроме IdHTTP поток не использует ничего кроме переменных. Не думаю что именно они и не освобождаются И как можно системе передать сообщение что типа убей поток и подчисти все, или дядя Бил это не предусмотрел? И по этой причине винда часто летит?
Автор: FireZone
Дата сообщения: 01.06.2006 10:16
Delphi6
ИМХО, лучше сделать так:
Код:
procedure TMyThread.TerminateConnection;
begin
// Вызываем DisconnectSocket только если fIdHTTP существует (не равен nil)
if Assigned(fIdHTTP) then fIdHTTP.DisconnectSocket
end;

procedure TMyThread.Execute;
begin
try
fIdHTTP := TIdHTTP.Create(nil); //создаём экземпляр TIdHTTP
try
...
fIdHTTP.Connect; // Это тот самый коннект, на котором происходит зависание
...
finally
FreeAndNil(fIdHTTP) //уничтожаем экземпляр
end
except
on E: Exception do AddToLogFile(E.ClassName + ': ' + E.Message);//Пишем в лог возникшие исключения
//Если был вызван DisconnectSocket, то метод Connect должен выскочить с исключением
end
end;
Автор: OdesitVadim
Дата сообщения: 01.06.2006 11:08
Delphi6

Цитата:
Получается если поток умирает то и компонент сдыхает. Или я не совсем правильно представляю что есть поток?

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

Эксперементы - это правильно! Побольше их.
Автор: Delphi6
Дата сообщения: 02.06.2006 02:12
Сори что долго не отвечал, был в 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
if Assigned(fIdHTTP) then begin
fIdHTTP.DisconnectSocket;
fIdHTTP.Free;
end;
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.WaitFor;
PCT.Free;
fResult := True;
end;

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

//======================================================
//TProxyChecker END
//======================================================
end.
Автор: ShIvADeSt
Дата сообщения: 02.06.2006 05:32

Цитата:
procedure TProxyCheckerThread.TerminateConnection;
begin
if Assigned(fIdHTTP) then begin
fIdHTTP.DisconnectSocket;
fIdHTTP.Free;
end;
end;

Попробуй fIdHTTP.FreeAndNil скорее всего не обнуляет. В каком то топике что то аналогичное рассматривалось.
Автор: Delphi6
Дата сообщения: 02.06.2006 06:27

Цитата:
Попробуй fIdHTTP.FreeAndNil скорее всего не обнуляет. В каком то топике что то аналогичное рассматривалось.

Вместо обеих строк или все же надо делать DisconectSocket?
Автор: FireZone
Дата сообщения: 02.06.2006 07:27
Delphi6
Если ты fIdHTTP создаёшь в конструкторе своего потокового объекта, то уничтожать его лучше в деструкторе, а TerminateConnection использовать только для разрыва соединения. Тут возможны грабли: если к моменту вызова DisconnectSocket соединение было разорвано к примеру сервером, или вообще не было установлено, то должно вылететь исключение и до fIdHTTP.Free дело не дойдёт. В этом случае, при уничтожении потокового объекта твой fIdHTTP останется болтаться в памяти до завершения приложения. Насчёт исключения надо глянуть исходники Indy; не уверен, что оно там есть, но возможно. Чтобы хоть что-то попробовать отследить, сделай кое-какие изменения:
Код:
procedure TProxyCheckerThread.TerminateConnection;
begin
if Assigned(fIdHTTP) then begin
try
fIdHTTP.DisconnectSocket;
FreeAndNil(fIdHTTP) //Функция вызывает метод Free и затем обнуляет указатель fIdHTTP,
//чтобы следующая проверка Assigned(fIdHTTP) дала ложь.
except
//Пишем в лог все исключения
on E: Exception do begin
AddToLogfile('ошибка в TerminateConnection: '+E.ClassName + ': ' + E.Message);
raise //Заново возбуждаем исключение, чтобы не изменить ход выполнения
end
end
end;
end;

procedure TProxyCheckerThread.Execute;
begin
try
fResultString := fIdHTTP.Get(fUrl);
except
//Error in connection
//Пишем в лог все исключения
on E: Exception do begin
AddToLogfile('ошибка в TProxyCheckerThread.Execute: '+E.ClassName + ': ' + E.Message);
Exit
end
end;
fResult := True;
end;
Автор: Delphi6
Дата сообщения: 02.06.2006 07:35
Вот сейчас сменил код fIdHTTP.Free на FreeAndNil(fIdHTTP) ошибок при тестировании не было (проводил два раза), вот только на 220 и 290 запуске/уничтожении потоки подвисли. Так что надо еще поработать. Мои подозрения таковы, потоки подвисают и не могут быть убиты. Сейчас еще раз пересмотрю код вызова и уничтожения потоков, на наличие ошибок.

FireZone
Вот сколько времени программирую а о конструкции
on E: Exception do бла бла
не слышал, довольно удобно, сейчас сделаю, посмотрим что у нас там будет. А как я уже говорил мне надо еще раз удостовериться что потоки именно повисают Я пробовал отследить в цикле количество рабочих (assigned) потоков и оно при ситуации "подвиса" равно было 15, как и запланировано. Получается что все таки потоки подвешены
Автор: OdesitVadim
Дата сообщения: 02.06.2006 10:41
Delphi6
О логгировании. Наиболее удобным (для меня по крайней мере) есть процедурка
OutputDebugString(msg:PAnsiChar); Для использования нужно подключить модуль Windows (он обычно уже подключен). Если запускаем с под Делфи, то сообщения можно подсмотреть в окошке Event Log. Там они будут предварятся префиксом ODS. Когда испытываеш на чужой машине, то качаеш DebugView (сам exe - 236k) отсюдова http://www.sysinternals.com/utilities/debugview.html
Он выводит все эти сообщения. Он также поможет, если пишеш сервисы или другие гадости.
Автор: FireZone
Дата сообщения: 02.06.2006 12:01
Delphi6
Еще небольшая рекомендация
Код: 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);
try //для гарантии уничтожения PCT в случае исключений
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
sleep(100); // для усыпления потока на 100 мс при каждом проходе, чтобы не загружать процессор холостым циклом
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.WaitFor;
finally
PCT.Free;
end;
fResult := True;
end;
Автор: Delphi6
Дата сообщения: 03.06.2006 04:28

Цитата:
sleep(100); // для усыпления потока на 100 мс при каждом проходе, чтобы не загружать процессор холостым циклом

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


Цитата:
качаешь DebugView

Сегодня запланировано у меня длительное тестирование при разных ситуациях. Программа думаю ценная так как позволяет просматривать состояние переменных (если их логировать) именно в потоках, чего стандартными способами добиться нельзя. Кстати ведение логов думаю будет рационально до того момента как программа из Альфа и Бета версий перейдет в Финальную.

А как вы поступаете с вашими проектами? Ведете ли логи, если да то как потом получаете их от ваших пользователей?

OdesitVadim
Ваша прога супер, удивительно но кажется ошибка в коде запуска одновременных потоков. После длительных запусков, алгоритму кажется что поток еще работает а на самом деле он уже убит, в логах видно! Так что начал пересматривать код.
Все замечания по коду приняты и уже внесены, спасибо
п.с. Скоро выложу результаты поисков

Выше указанный текст не совсем правда, вот что получил после теста:


Код: procedure TProxyChecker.Execute;
var
PCT: TProxyCheckerThread;
StartTime: Integer;
timePass: Integer;

i: integer;
begin
i := Random(1000000);
try
OutputDebugString(pChar('Information: #'+ IntToStr(fThreadIndex) +'('+ IntToStr(i) + ') Start Working!'));
//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
Sleep(100);
timePass := GetTickCount-StartTime;
//Wait for result or timeout
end;
OutputDebugString(pChar('Information: #'+ IntToStr(fThreadIndex) +'('+ IntToStr(i) + ') Timrout!'));
//Checking if result exist and analyze it
if PCT.GetResult then begin
fProxyLatency := timePass;
OutputDebugString(pChar('Information: #'+ IntToStr(fThreadIndex) +'('+ IntToStr(i) + ') Before Analyze!'));
AnalyzeResult(PCT.GetResultString);
OutputDebugString(pChar('Information: #'+ IntToStr(fThreadIndex) +'('+ IntToStr(i) + ') After Analyze!'));
end else
fProxyType := ptBad;
OutputDebugString(pChar('Information: #'+ IntToStr(fThreadIndex) +'('+ IntToStr(i) + ') Start Term!'));
//Terminate and wait until thread will finish its working (may cause hang on)
PCT.TerminateConnection;
PCT.WaitFor;
finally
FreeAndNil(PCT);
end;
OutputDebugString(pChar('Information: #'+ IntToStr(fThreadIndex) +'('+ IntToStr(i) + ') Finish Term!'));
fResult := True;
end;
Автор: OdesitVadim
Дата сообщения: 03.06.2006 12:27

Цитата:
А как вы поступаете с вашими проектами? Ведете ли логи, если да то как потом получаете их от ваших пользователей?

Я в тексте программы ставлю вызов процедурки типа

Код:
procedure debug(s:string);
begin
{$ifdef debug}
Outputdebugstring(pAnsiChar(s));
{$else}
//а здесь запись в файл по необходимости.
{$endif}
end;
Автор: Delphi6
Дата сообщения: 04.06.2006 06:14
Ну думаю вопрос можно считать почти закрытым. Сейчас провожу очень большой тест длительностью в 3 часа минимум, после него можно будет с точностью говорить насколько все правильно выполнено.

Хочу подвести итоги, дабы второй пользователь на данном форуме не наступил на теже грабли. Главное надо пользоваться процедурой FreeAndNil() вместо стандартного Free. Я лично теперь для всех объектов его буду использовать, для подстраховки

Второе, нужно все подозрительные части кода ставить в try except и обязательно все подозрительные части где происходит создание объекта в try finally что бы быть уверенным что он удалиться даже при случае ошибки.

В третьих посоветую многим воспользоваться программой которую мне предложил OdesitVadim очень сильная вещь, особенно если тестирование будет производиться уже с готовым EXE-шником да и на другом компьютере. Превосходство этой программы от обыкновенной записи в файл следующие, вы в реал таиме видите все, можно не беспокоиться что файл после записи будет закрыт правильно (если открывать и тут же закрывать файл, понизиться производительность программы).

Также важно то что надо избегать использования TerminateThread так как при этом большие потери памяти. А зачищаться эта память будет только после отключения программы что ни есть хорошо, если в программе происходит очень много (больше 1000) аварийных уничтожений.

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

п.с. Ошибка была в теле AnalyzeResult, при некоторых ситуациях код обращался к не существующему элементу массива (динамического).
Автор: FireZone
Дата сообщения: 04.06.2006 15:39

Цитата:
FreeAndNil() вместо стандартного Free
Для красоты стиля всё же лучше использовать Free и только в специфических случаях FreeAndNil. А именно тогда, когда какие-то участки кода ориентируются на существование объекта по ненулевому значению указателя.
Цитата:
Второе, нужно все подозрительные части кода ставить в try except
И не забывать правильно обрабатывать исключения, иначе можно от самого себя скрыть важную ошибку и долго искать глюки в коде и утечки памяти и ресурсов. Например, самой глупой опрометчивостью будет намеренно прятать ошибки Access Violation в блоки try ... except end.
Цитата:
В третьих посоветую многим воспользоваться программой которую мне предложил OdesitVadim
И я попробую.
Цитата:
можно не беспокоиться что файл после записи будет закрыт правильно

Чтобы не беспокоитсья, используем опять же try-finally:
Код: reset(f);
try
...
finally
closefile(f)
end
Автор: Delphi6
Дата сообщения: 05.06.2006 02:04
FireZone
Очень хорошие и интересные коменты, со всем согласен, кроме FreeAndNil() IMHO лучше всегда им пользоваться

А на счет try except вы правильно подметили, возможно случайно запутать самого себя. Так что лучше все легировать в файл и OutputDebugString.

Вчерашнее тестировании показало себя как нельзя лучше. Все удачно прошел с 3000 потоками Так что объявляю тему закрытой

Всем спасибо
Автор: vserd
Дата сообщения: 05.06.2006 12:25

Цитата:
Главное надо пользоваться процедурой FreeAndNil() вместо стандартного Free.

Главное это нужно понимать что делая объекту MyVar.Free мы освобождаем память, указатель на которую храниться в переменной MyVar, но не обнуляем указатель MyVar(значение переменной MyVar). А если эта переменная находится в области видимости отличной от локальной, и идет проверка на Nil / not NIL, тогда нужно писать два предложения вместо одного.
MyVar.Free;
MyVar := Nil;

А включать дополнительный модуль ради одной этой функции, является излишеством.

Для понимания посмотри на код FreeAndNil:

procedure FreeAndNil(var Obj);
var
P: TObject;
begin
P := TObject(Obj);
TObject(Obj) := nil; // clear the reference before destroying the object
P.Free;
end;

Что касаемо OutputDebugString. Не забывай что для пользования этой функцией тебе нужно будет иметь соответствующие права доступа (Администратор по умолчанию, для других пользователей настраевается). А в корпоративной среде все сидят под ограниченной учеткой и таких прав у них обычно нет. Вывод в файл обычно не зависит от таких прав и пользователю гораздо легче объяснить по телефону откуда скопировать файл лога и как его отослать по почте разарботчику чем как сграбить данные выводимые по ODS.
Автор: DavidMoore
Дата сообщения: 09.07.2008 11:51
Господа программисты подскажите новечку плис.
я только начал разбираться с потоками и вот какая штука возникла:
у меня есть форма с скроллбаром, 2 потока, первый двигает позицию влево, второй соответственно вправо. При любых приоритетах почему то выйгрывает первый поток. так и должно быть? или я чтото упустил?
бррр... извиняюсь задаю тупые вопросы изза своей невнимательности. всё ок.

Страницы: 12

Предыдущая тема: Вопрос по FOXPRO


Форум Ru-Board.club — поднят 15-09-2016 числа. Цель - сохранить наследие старого Ru-Board, истории становления российского интернета. Сделано для людей.