Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Delphi программирование > Общие вопросы Delphi
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 21.11.2011, 23:58   #1
Человек_Борща
Старожил
 
Аватар для Человек_Борща
 
Регистрация: 30.12.2009
Сообщений: 11,430
По умолчанию При освобождении обьекта TThread, он вешает всю программу. При этом код Execute у TThread уж выполнился..

Доброго времени суток...

Есть таймер, который каждую пытается запустить поток(TThread), предварительно проверив его на nil:
Код:
procedure DoPingTimer(Sender: TObject);
begin
  if (PingThr = nil) then
  begin
    PingThr := TPingThread.Create(True);
    PingThr.Timeout := AppInfo.ServerPingTimeout;
    PingThr.IP := AppInfo.ServerIP;
    PingThr.Port := AppInfo.ServerPort;
    PingThr.FreeOnTerminate := True;
    PingThr.Priority := tpHigher;
    PingThr.OnTimeout := OnPingTimeout;
    PingThr.Resume;
  end;
end;

Поток запускается с флагом FreeOnTerminate:=True
В потоке выполняется код "пинга" UDP сервера:
Код:
procedure TPingThread.UDPRead(Sender: TObject; AData: TStream;
  ABinding: TIdSocketHandle);
var
  Buff: array[0..255] of Char;
  s: string;
begin
  if Terminated then
    Exit;
  aData.Read(Buff, Length(Buff));
  s := StrPas(Buff);
  if (s = Server_Answer) then
  begin
    if Terminated then
      Exit;
    Result := True;
    SetEvent(Event);
    if Terminated then
      Exit;
  end
  else
    Result := false;
  if Terminated then
    Exit;
end;

{ TPingThread }

procedure TPingThread.SetProc(aVal: TOnTimeout);
begin
  fOnTimeOut := aVal;
end;

procedure TPingThread.Execute;
var
  Data: array of Char;
  W: Cardinal;
begin
  inherited;
  Event := CreateEvent(nil, True, False, nil);
  UDP := TIdUDPServer.Create(nil);
  try
    if Terminated then
      Exit;
    UDP.ThreadedEvent := True;
    UDP.OnUDPRead := UDPRead;
    SetLength(data, Length(Server_Request));
    if Terminated then
      Exit;
    StrPCopy(@data[0], Server_Request);
    UDP.SendBuffer(fIP, fPort, data[0], Length(data));
      if WaitForSingleObject(Event, fTimeout * 1000) = WAIT_TIMEOUT then
      begin
        if Terminated then
          Exit;
        Synchronize(fOnTimeout);
      end;
  finally
    FreeAndNil(UDP);
    CloseHandle(Event);
  end;
end;
Проблема в том, что Free не выполняется вообще.
программа прыгает на код Synchronize(fOnTimeout);, процедура вызвается. Все проходит нормально. Как и я и оидал, НО ссылка на поток(PingThr) не равна nil. Код потока закончился и делать ему уже нечего, по этому он должен прибится до конца. Увы его что-то держит от этого. И повторно пустить поток не получается.

А если просто вызвать FreeAndNil, вешается вся программа.


Помогите, пожалуйста!
Человек_Борща вне форума Ответить с цитированием
Старый 22.11.2011, 00:14   #2
Аватар
Старожил
 
Аватар для Аватар
 
Регистрация: 17.11.2010
Сообщений: 18,922
По умолчанию

Я думаю ваш поток ни что не держит, память освобождена, а вот PingThr не nil, и откуда он там возмется после завершения потока?

add

Можно попробовать поток запускать с FreeOnTerminate=False. Перед самым завершением он какую-то переменную в основном потоке в True устанавливает с использованием Synchronize. Таймер анализирует эту переменную, и если она True, то ждет завершение потока WaitFor, после чего Free и создание нового потока
Если бы архитекторы строили здания так, как программисты пишут программы, то первый залетевший дятел разрушил бы цивилизацию

Последний раз редактировалось Аватар; 22.11.2011 в 00:34.
Аватар вне форума Ответить с цитированием
Старый 22.11.2011, 06:02   #3
Johnson
кривокодер ;)
Форумчанин
 
Аватар для Johnson
 
Регистрация: 20.06.2008
Сообщений: 707
По умолчанию

Поток и не должен удалиться...
Создание потока - дело весьма длительное. Поэтому, потоку при завершении ставится статус Terminated, но он не удаляется.

А вообще, логика программы слегка нарушена.
Вам настолько важно пинговаться РОВНО через секунду?

Код:
while not terminated do begin
	// тут пингуемся.
	sleep(1000);
end;
вот для примера мой экзекут из потока в библиотеке)) много лишнего вырезать некогда, но суть будет понятна...
Код:
{ TSock }
procedure TSock.Execute;
var
  len:Word;
  pck,CMD,PARAM,Tmp:string;
begin
  S:=TClientSocket.Create(nil);
  S.Port:=6661;
  S.Host:='127.0.0.1';
  S.ClientType:=ctBlocking;
  s.OnConnect:=SckConnect;
  s.OnDisconnect:=SckDisconnect;
  s.OnError:=SckError;
  // Бесконечный цикл сделан для того, чтоб клиент конектился к серверу бота после
  // обрыва связи.
  while True do begin
    // Подключение к серверу бота
    S.Open;
    Sleep(50);
    while not S.Socket.Connected do Sleep(10);
    // Синхронизация для передачи параметров сокета основному циклу

    // Прием пакетов от сервера бота
    // Фактически, пакет в сокет прилетает такого вида:
    // xx_CMD_PARAM
    // где хх - это длина пакета (нужна для полного приема пакета. буфер сокетов
    // имеет привычку загружаться медленно и стэковать пакеты)

    while S.Socket.Connected do begin
      Sleep(10);//чтоб процессор не грузить...
      // Проверяем, есть ли что-то в буфере приема сокета...
      if s.Socket.ReceiveLength<2 then Continue;
      // Принимает первые два байта длины пакета. Они удаляются из буфера
      S.Socket.ReceiveBuf(len,2);
      while len>s.Socket.ReceiveLength do Sleep(5);
      // Устанавливаем длину буфера
      SetLength(pck,len);
      // Принимает собственно сам пакет без байтов длины
      s.Socket.ReceiveBuf(pck[1],len);
      if (pck[1]<>'_')or(Length(pck)<2) then Continue;
      // Сдесь уже пакет будет такого вида:
      // _CMD_PARAM
      Delete(pck,1,1);//удаляем префикс
      CMD:=Copy(pck,1,Pos('_',pck)-1);
      Delete(pck,1,Pos('_',pck));
      PARAM:=pck;
      // К параметру (пакету данных игры) дописываем два байтa ЕГО длины.
      SetLength(Tmp,2);
      Len:=Length(PARAM)+2;
      Move(Len,Tmp[1],2);
      PARAM:=Tmp+PARAM;

      // Теперь пакет готов к отправке в сторону клиента/сервера игры.
      if UpperCase(CMD)='TOCLIENT' then begin
        SendPacketToClient(PARAM[1],Length(PARAM));
      end else if UpperCase(CMD)='TOSERVER' then begin
        SendPacketToServer(PARAM[1],Length(PARAM));
      end;
    end;
  end;
end;
"А как написать праграму?, "ришыти задачьку очинь нада" ©с форума. Жить становится интереснее, жить становится веселее...
{Быть или не быть} {Неуспешный суицид}

Последний раз редактировалось Johnson; 22.11.2011 в 06:05.
Johnson вне форума Ответить с цитированием
Старый 24.11.2011, 04:44   #4
Человек_Борща
Старожил
 
Аватар для Человек_Борща
 
Регистрация: 30.12.2009
Сообщений: 11,430
По умолчанию

Всеравно не догоняю..
Вот в чём смысл..
При запуске ПО, моя программа запускает поток пинга, а тот пингует запущенное ПО.
Как только ПО не пингуется сработывает действие(Рестарт - вещается не пингующееся ПО, и все перезапускается по новой).

Как это зделать?

Сейчас у меня все через "пятую точку опоры".
Прри создании обьекта ПО, создаётся таймер, который проверяет не равен ли поток nil'у нели, равен запускает его. Тот отработывает 1 раз и вырубается. И так, пока ПО будет не пинговаться.

Но я знаю что можно и без этого гемора. КАК?

Конкретно, хочу выкинуть таймер, который пытается запускать поток пинга.

Ну или как по меньше гемора, чтобы поток работал постоянно, до того как перестаёт пинговаться ПО, при этом срабатывает действие.
Человек_Борща вне форума Ответить с цитированием
Старый 24.11.2011, 05:56   #5
Johnson
кривокодер ;)
Форумчанин
 
Аватар для Johnson
 
Регистрация: 20.06.2008
Сообщений: 707
По умолчанию

ПО, которое проверяете - Ваше?
Поток исполняется в нем?
"А как написать праграму?, "ришыти задачьку очинь нада" ©с форума. Жить становится интереснее, жить становится веселее...
{Быть или не быть} {Неуспешный суицид}
Johnson вне форума Ответить с цитированием
Старый 24.11.2011, 11:17   #6
Человек_Борща
Старожил
 
Аватар для Человек_Борща
 
Регистрация: 30.12.2009
Сообщений: 11,430
По умолчанию

Нет, ПО(Консольная сервер-программа) которое нужно пропинговать - не моё.
Знаю как с ним общаться, по этому возможен пинг.

Моя программа создаёт Object программы в ObjectList, в это обьекте 2 потока, 1 основнной, который запускается и запускает ПО и ждёт его завершения, а второй - это поток пинга, который запускается когда запускается первый поток, и останавливается когда останавливается первый.

Задача пингующего потока, среагировать на недоступность сервер-сокета в программе, и прибить повисшую консоль сервера, основной поток, себя любимого и перезапустить всё по новой.

Последний раз редактировалось Человек_Борща; 24.11.2011 в 11:20.
Человек_Борща вне форума Ответить с цитированием
Старый 28.11.2011, 01:07   #7
Человек_Борща
Старожил
 
Аватар для Человек_Борща
 
Регистрация: 30.12.2009
Сообщений: 11,430
По умолчанию

Моё состояние сейчас примерно такое:

Всеравно, хоть убей поток не вешается. И при этом ни *** не делает.

Запускаю всё это дело так:
Код:
procedure Start;
begin
....
      if (PingThr = nil) then
      begin
        PingThr := TPingThread.Create(True);
        PingThr.Timeout := AppInfo.ServerPingTimeout;
        PingThr.IP := AppInfo.ServerIP;
        PingThr.Port := AppInfo.ServerPort;
        PingThr.OnTimeout := OnPingTimeout;
        PingThr.MaxAttempts := AppInfo.ServerPingMaxAttempts;
        PingThr.Priority := tpHigher;
        PingThr.Resume;
      end;
...
Останавливаю:
Код:
procedure Stop;
begin
      if not (PingThr = nil) then
      begin
        FreeAndNil(PingThr); //программа вешается 
      end;
Код потока:
Код:
const
  Server_Request = #255#255#255#255'i'#0;
  Server_Answer = 'яяяяj';

type
  TOnTimeout = procedure of object;

type
  TPingThread = class(TThread)
  private
    fResult: Boolean;
    fTimeout: Integer;
    fMaxAttempts: Integer;
    fIP: string;
    fPort: Integer;
    fEvent: THandle;
    fOnTimeOut: TOnTimeout;
    property Event: THandle read fEvent write fEvent;
    procedure SetProc(aVal: TOnTimeout);
  protected
    procedure Execute; override;
  public
    UDP: TIdUDPServer;
    property MaxAttempts: Integer read fMaxAttempts write fMaxAttempts;
    property Result: Boolean read fResult write fResult default False;
    property Timeout: Integer read fTimeout write fTimeout default 10;
    property IP: string read fIP write fIP;
    property Port: Integer read fPort write fPort;
    property OnTimeout: TOnTimeout read fOnTimeOut write SetProc default nil;
    procedure UDPRead(Sender: TObject; AData: TStream;
      ABinding: TIdSocketHandle);
  end;

implementation

procedure TPingThread.UDPRead(Sender: TObject; AData: TStream;
  ABinding: TIdSocketHandle);
var
  Buff: array[0..255] of Char;
  s: string;
begin
  if Terminated then
    Exit;
  aData.Read(Buff, Length(Buff));
  s := StrPas(Buff);
  if (s = Server_Answer) then
  begin
    if Terminated then
      Exit;
    Result := True;
    SetEvent(Event);
  end
  else
    Result := false;
end;

{ TPingThread }

procedure TPingThread.SetProc(aVal: TOnTimeout);
begin
  fOnTimeOut := aVal;
end;

procedure TPingThread.Execute;
var
  Data: array of Char;
  Attemptcount: Integer;
begin
  inherited;
  Event := CreateEvent(nil, True, False, nil);
  UDP := TIdUDPServer.Create(nil);

  UDP.ThreadedEvent := True;
  UDP.OnUDPRead := UDPRead;
  SetLength(data, Length(Server_Request));
  StrPCopy(@data[0], Server_Request);
  Attemptcount := 1;

  while not terminated do
  begin
    if Terminated then
      Exit;
    UDP.SendBuffer(fIP, fPort, data[0], Length(data));
    if WaitForSingleObject(Event, fTimeout * 1000) = WAIT_TIMEOUT then
    begin
      if (Attemptcount < fMaxAttempts) then
        Inc(Attemptcount);

      if Terminated then
        Exit;

      if (Attemptcount = fMaxAttempts) then
      begin
        FreeAndNil(UDP);    
        CloseHandle(Event);
        Terminate;
        Synchronize(fOnTimeout);
      end;
    end;
  end;
end;

end.
Не понимаю, все обьекты исп. потоком - уничтожены.
Он больше ничего не делает.

При вызове procedure Stop, пытаюсь вызвать FreeAndNil, т.к. freeonterminate=false. В этот момент у потока свойство FFinished = TRUE.
Вся программа просто висит. ПОЧЕМУ?

Последний раз редактировалось Человек_Борща; 28.11.2011 в 01:11.
Человек_Борща вне форума Ответить с цитированием
Старый 28.11.2011, 09:33   #8
Stilet
Белик Виталий :)
Старожил
 
Аватар для Stilet
 
Регистрация: 23.07.2007
Сообщений: 57,097
По умолчанию

Цитата:
while not terminated do
begin
if Terminated then
Exit;
Смысл сего?
Цитата:
Вся программа просто висит. ПОЧЕМУ?
А пошаговка дает что-нить?
I'm learning to live...
Stilet вне форума Ответить с цитированием
Старый 28.11.2011, 11:17   #9
Человек_Борща
Старожил
 
Аватар для Человек_Борща
 
Регистрация: 30.12.2009
Сообщений: 11,430
По умолчанию

я же говорю, программа потока полностью завершена.
Прыгает на конец процендуры Execute. И не прибивается.

Специально и сделал так:


Код:
      if (Attemptcount = fMaxAttempts) then
      begin
        FreeAndNil(UDP);    
        CloseHandle(Event);
        Terminate;
        Synchronize(fOnTimeout);
      end;
хм после всех операций в выше указанном куске кода.
Оно прыгает на метод start(Ну второй поток оно не создаёт, зачем).
Затем назад в метод Execute, в цикл While not terminated do , далее не может в него войти. И пытается прибиться при помощи FreeOnTerminate. Результат, вся программа в накауте.
Человек_Борща вне форума Ответить с цитированием
Старый 28.11.2011, 12:33   #10
Aliens_wolfs
Форумчанин
 
Регистрация: 16.12.2009
Сообщений: 902
По умолчанию

В твоем коде.
Код:
procedure TPingThread.Execute;
var
  Data: array of Char;
  Attemptcount: Integer;
begin
  inherited; Этот параметр там не нужен

Лучше сделай отдельное создание и освобождение ресурсов не в процессе работы потока
добавь в параметры потока следующие строки
Код:
  constructor Create; virtual;
    destructor Destroy; override;
В создании потока сделай так
Код:
constructor TPingThread.Create;
begin
  inherited Create(false); //запуск после создания если true то запуск через Resume
  FreeOnTerminate := True; //при остановке Terminate поток сам уничтожиться при этом сработает процедура Destroy
  Event := CreateEvent(nil, True, False, nil);
  UDP := TIdUDPServer.Create(nil);
  UDP.ThreadedEvent := True;
  UDP.OnUDPRead := UDPRead;
end
сделай процедуру по уничтожению при остановке все будет освобождаться
Код:
destructor TPingThread.Destroy;
begin
       FreeAndNil(UDP);  либо UDP.free;
       CloseHandle(Event);
inherited;
end;
Ну и сама работа потока
Код:
procedure TPingThread.Execute;
var
  Data: array of Char;
  Attemptcount: Integer;
begin
  SetLength(data, Length(Server_Request));
  StrPCopy(@data[0], Server_Request);
  Attemptcount := 1;

  while not terminated do
  begin
    UDP.SendBuffer(fIP, fPort, data[0], Length(data));
    if WaitForSingleObject(Event, fTimeout * 1000) = WAIT_TIMEOUT then
    begin
      if (Attemptcount < fMaxAttempts) then
        Inc(Attemptcount);

      if (Attemptcount = fMaxAttempts) then
     begin
        Synchronize(fOnTimeout); либо Terminate засунь в эту процедуру fOnTimeout отсюда убери ту или иную строку
        Terminate;
  end;
    end;
  end;
end;

Последний раз редактировалось Aliens_wolfs; 28.11.2011 в 15:23.
Aliens_wolfs вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
при закрытии потока(Tthread) посылать на форму код ответа или сообщение Человек_Борща Общие вопросы Delphi 2 14.12.2010 21:19
Работа с TThread pesi Общие вопросы Delphi 2 09.08.2010 14:12
TThread Vladislav_I Общие вопросы Delphi 0 30.04.2010 19:47
Ошибка при OpenDialog.Execute ymka2 Общие вопросы Delphi 10 27.11.2009 14:15
TThread и GUI Freezer Общие вопросы Delphi 8 05.08.2009 11:50