Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Начинающим";
Текущий архив: 2006.04.30;
Скачать: [xml.tar.bz2];

Вниз

Динамичесоке создание компоненты в потоке - не получается   Найти похожие ветки 

 
kay ©   (2006-04-12 12:23) [0]

Есть компонента idTcpClient, которая при отсутствии соединения вешает программу.
Решил создать тред, который содержит эту компоненту, но не знаю какого Owner"а писать при создании. Если родителя сделать основную форму, то вылетает ошибка при записи в адрес 00000000

constructor TtcpThread.Create;
begin
 inherited Create(true);
 tcpClient.Create(ovpnRestarter);
 tcpClient.OnStatus:=tcpClientStatus;
end;


заголовок

type
 TtcpThread = class(TThread)
   private
     procedure tcpClientStatus(ASender: TObject; const AStatus: TIdStatus;
       const AStatusText: String);
   protected
     procedure Execute; override;
   public
     tcpClient: TIdTCPClient;
     constructor Create;
     destructor Destroy; override;
 end;


 
sniknik ©   (2006-04-12 12:33) [1]

> но не знаю какого Owner"а писать при создании.
nil пиши, и обязательно тогда уничтожай его в деструкторе

основная форма в потоке фигурировать никак не должна


 
kay ©   (2006-04-12 12:52) [2]

может есть компонента уже работающая в потоке. мнепросто нужно проверять соединение с сервером. в противном случае идёт процедура работы с соединением.


 
Сергей М. ©   (2006-04-12 13:11) [3]


> kay ©   (12.04.06 12:52) [2]



> просто нужно проверять соединение с сервером


Поток здесь вовсе не обязателен.

Подойдет любой компонент, использующий асинхронный неблокирующий режим, например, TClientSocket.

TIdTCPClient же использует синхронный блокирующий режим, но и при его использовании необязательна организация дополнительного потока - достаточно бросить на форму компонент TIdAntifreeze.


 
kay ©   (2006-04-12 13:28) [4]

спасибо! а то я уже реализовал тред, но есть одно НО!

после того как запустить тред (Resume), выйти из него (Suspend) и завершить приложение, то возникает ошибка записи памяти 00000000.

вот код завершения треда:
 if tcpClient <> nil then
   tcpClient.Free;

 inherited;


 
kay ©   (2006-04-12 13:28) [5]

спасибо! а то я уже реализовал тред, но есть одно НО!

после того как запустить тред (Resume), выйти из него (Suspend) и завершить приложение, то возникает ошибка записи памяти 00000000.

вот код завершения треда:
 if tcpClient <> nil then
   tcpClient.Free;

 inherited;


 
Сергей М. ©   (2006-04-12 13:31) [6]


> вот код завершения треда:


Где этот код размещен ? В каком методе ?


 
kay ©   (2006-04-12 13:33) [7]

destructor TtcpThread.Destroy;
begin

 if tcpClient <> nil then
   tcpClient.Free;

 inherited;
end;

забыл всё скопировать...

баг возникает только если тред запустить

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


 
kay ©   (2006-04-12 13:35) [8]

ну и на всякий случай
procedure TtcpThread.Execute;
begin
 tcpClient := TIdTCPClient.Create(nil);
 tcpClient.OnStatus:=tcpClientStatus;

 tcpClient.Host:="www.google.com";
 tcpClient.Port:=80;
 tcpClient.Connect();
end;


код запуска треда в самой программе:
tcp.Resume;
код остановки треда в программе:
tcp.Suspend;

после выхода из программы запускается деструктор и ошибка записи памяти 00000000


 
kay ©   (2006-04-12 13:38) [9]

может надо не Suspend, а Terminate?


 
kay ©   (2006-04-12 13:40) [10]

с Terminate такая же проблема =(


 
Сергей М. ©   (2006-04-12 13:44) [11]


> для стандартного клиента нужно DNS lookup делать, что не
> хочется


В твоем случае любой компонент будет пытаться выполнять разрешение имени хоста в его IP-адрес - ты ведь указываешь хост не по адресу, а по имени ..


> баг возникает только если тред запустить


На какой конкретно строчке возникает исключение ?


 
kay ©   (2006-04-12 13:51) [12]

1) всё равно уж попробую тред добить. не получится - буду использовать TcpClient от борланда.

2)inherited

после нажатия F7 почемуто переходит на end метода TtcpThread.Execute;


 
sniknik ©   (2006-04-12 13:53) [13]

> tcpClient.OnStatus:=tcpClientStatus;
где и как описана? (проверь без нее)

потом если у тебя саздание в Execute то там и освождение делай... так примерно

procedure TtcpThread.Execute;
begin
 with TIdTCPClient.Create(nil) do
   try
      //OnStatus:=tcpClientStatus; для теста отключить

      Host:="www.google.com";
      Port:=80;
      Connect();
   finally
      Free;
   end;
end;


(тогда и constructor и destructor переопределять лишнее, пойдут стандартные, писать меньше, код короче, понятнее...)


 
kay ©   (2006-04-12 13:55) [14]

ураЙ! получилось! вместо просто tcpClient.Free;

сделал
     tcpClient.Disconnect;
     tcpClient.Free;

ибо тред всё ещё работал и выполнялось убиение


 
Сергей М. ©   (2006-04-12 13:56) [15]

Что-то я не понял ..

Объект TIdTCPClient ты создаешь дважды - первый раз в конструкторе треда, в второй в теле Execute ...


 
kay ©   (2006-04-12 13:57) [16]

в конструкторе треда я его не создаю.
   public
     tcpClient: TIdTCPClient;
     destructor Destroy; override;


 
Сергей М. ©   (2006-04-12 13:59) [17]


> sniknik ©   (12.04.06 13:53) [13]


Так он утечку схлопочет, ибо поток может быть приостановлен при выполнении блокирующего метода Connect(), после чего объект TtcpThread уничтожен .. При этом до finally (где выполняется разрушение TIdTCPClient) дело, разумеется, не доходит


 
Сергей М. ©   (2006-04-12 14:01) [18]


> в конструкторе треда я его не создаю


Цитирую самый первый твой пост :


> constructor TtcpThread.Create;
> begin
>  inherited Create(true);
>  tcpClient.Create(ovpnRestarter);
>  tcpClient.OnStatus:=tcpClientStatus;
> end;


Выделенной жирным это что, спрашивается ?


 
kay ©   (2006-04-12 14:06) [19]

я его уже убрал, это первая версия была. всем спасибо. тему можно закрывать.


 
Сергей М. ©   (2006-04-12 14:25) [20]

И тем не менее принудительное терминирование "висящего" треда (способ, выбранный тобой) - крайний вариант, которого всегда следует избегать.


 
kay ©   (2006-04-12 14:27) [21]

я сначала DIsconnect делаю, тред завершает работу - и далее всё по маслу


 
Сергей М. ©   (2006-04-12 14:33) [22]


> тред завершает работу - и далее всё по маслу


Далеко не "по маслу".

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


 
kay ©   (2006-04-12 15:26) [23]

блин. действительно не всё так гладко.

значит объясняю как всё есть. при создании формы
tcp:=TtcpThread.Create;

при завершении приложения:
 tcp.tcpClient.Disconnect;
 tcp.Free;


далее сам тред:
unit thread;

interface

uses
 Windows, SysUtils, Classes, IdTCPClient, IdComponent;

type
 TtcpThread = class(TThread)
   private
     procedure tcpClientStatus(ASender: TObject; const AStatus: TIdStatus;
       const AStatusText: String);
   protected
     procedure Execute; override;
   public
     tcpClient: TIdTCPClient;
     destructor Destroy; override;
     constructor Create;
 end;

implementation

uses main;

constructor TtcpThread.Create;
begin
 inherited Create(true);
 tcpClient := TIdTCPClient.Create(nil);
end;
//******************************************************************************
destructor TtcpThread.Destroy;
begin
 tcpClient.Free;
 inherited;
end;
//******************************************************************************
procedure TtcpThread.Execute;
begin
 try
   tcpClient.OnStatus:=tcpClientStatus;
   tcpClient.Host:="www.google.com";
   tcpClient.Port:=80;
   tcpClient.Connect();
   tcpClient.Disconnect();
 except
   on E: Exception do
     begin
       ovpnRestarter.logAdd(E.Message);
     end;
 end;
end;
//******************************************************************************
procedure TtcpThread.tcpClientStatus(ASender: TObject;
 const AStatus: TIdStatus; const AStatusText: String);
begin
 ovpnRestarter.logAdd(AStatusText);
end;
//******************************************************************************
end.


вроде всё нормально, но иногда возникает зависон все системы. проц на 100% загружен. не понимаю в чём проблема. при вызове метода tcp.tcpClient.Disconnect; в треде возникает Exception и все ошибки обрабатываются.


 
kay ©   (2006-04-12 15:53) [24]

забыл, что есть 2 кнопик. start и stop

tcp:TtcpThread;

на запуске tcp.Resume;

на stop:
tcp.tcpClient.Disconnect;


 
Сергей М. ©   (2006-04-12 16:00) [25]

Ну нельзя же так бессовестно врать !

Цитирую тебя :


> я его уже убрал


И опять вижу при этом  в [23]


> constructor TtcpThread.Create;
> begin
>  inherited Create(true);
>  tcpClient := TIdTCPClient.Create(nil);
> end;


 
sniknik ©   (2006-04-12 16:08) [26]

> при завершении приложения:
>  tcp.tcpClient.Disconnect;
>  tcp.Free;
тут тебе надо не tcp.tcpClient.Disconnect; делать (что неправильно, и вообще Disconnect лишний), а tcp.WaitFor; ставить.
а лучше FreeOnTerminate в создании установить в true и "забыть" о потоке сразу после создания... а то иначе смысл самого потока теряеш.

и потом убрать прямые вызовы из другого потока.
> ovpnRestarter.logAdd(AStatusText);
сделать через синхронизацию. (сообщения/критическую секцию)

> И опять вижу при этом  в [23]
но в execute то при этом уже нету ;), все нормально тут с с созданием (лучше бы как у меня сделать по принципу... но и так пойдет).


 
sniknik ©   (2006-04-12 16:15) [27]

> забыл, что есть 2 кнопик. start и stop

> tcp:TtcpThread;

> на запуске tcp.Resume;

> на stop:
> tcp.tcpClient.Disconnect;

а смысл? "на stop" конект уже проверится и "отвалится" (execute кончится), что ты вообще хочеш сделать?


 
Сергей М. ©   (2006-04-12 16:18) [28]


> в execute то при этом уже нет


Зато появился Disconnect() сразу после Connect().
Ну и зачем нужна эта бестолковая логика ?


> лучше бы как у меня сделать по принципу


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


 
Сергей М. ©   (2006-04-12 16:19) [29]

В целом - бардак в логике и в выборе инструментов/алгоритмов ее реализации.


 
kay ©   (2006-04-12 16:22) [30]

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


 
sniknik ©   (2006-04-12 16:34) [31]

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

unit thread;

interface

uses Classes, SysUtils, IdTCPClient, IdComponent;

type
 TtcpThread = class(TThread)
 private
   tcpClient: TIdTCPClient;
   msg: string;
   procedure SendMessage;
   procedure tcpClientStatus(ASender: TObject; const AStatus: TIdStatus; const AStatusText: String);
 protected
    procedure Execute; override;
 end;

implementation

uses main;

procedure TtcpThread.SendMessage;
begin
 ovpnRestarter.logAdd(msg);
end;

procedure TtcpThread.tcpClientStatus(ASender: TObject; const AStatus: TIdStatus; const AStatusText: String);
begin
 msg:= AStatusText;
 Synchronize(SendMessage);
end;

procedure TtcpThread.Execute;
begin
FreeOnTerminate:= true;

with TIdTCPClient.Create(nil) do
  try
    try
      OnStatus:= tcpClientStatus;
      Host:= "www.google.com";
      Port:= 80;
      Connect();
    except
      on E: Exception do begin
        msg:= E.Message;
        Synchronize(SendMessage);
      end;
    end;
  finally
    Free;
  end;
end;

end.


и все.

пример вызова:
TtcpThread.Create(false);
и тоже все, ничего более, никаких извне вмешательств в компоненты потока, никаких закрытий, освождений... и т.д. это единственная (в этом случае) доступная тебе функция потока, его создание. (повесь на кнопку start)


 
Сергей М. ©   (2006-04-12 16:34) [32]


> чтобы постоянно не освобождать память


Что значит "постоянно" ? Какую память ?


> при выходе


При каком "выходе" ? Выходе откуда ?


 
kay ©   (2006-04-12 16:34) [33]

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


 
kay ©   (2006-04-12 16:43) [34]


> пример вызова:
> TtcpThread.Create(false);
> и тоже все, ничего более, никаких извне вмешательств в компоненты
> потока, никаких закрытий, освождений... и т.д. это единственная
> (в этом случае) доступная тебе функция потока, его создание.
>  (повесь на кнопку start)

я так понял при выходе их приложения вызывается метод Terminate? А переменная FreeOnTerminate говорит о том, что при выходе из приложения метод Free не понадобится?


 
kay ©   (2006-04-12 17:04) [35]

как убить тред в любой момент времени, чтобы от него ничего не осталось?


 
sniknik ©   (2006-04-12 17:09) [36]

> я так понял при выходе их приложения вызывается метод Terminate?
нет. Terminate нужен (сдесь, в TThread)  чтобы прервать и завершить Execute (у тебя этого не используется, нормально на это проверки в цикле ставить... но у тебя и цикла нет одноразовое действие), по завершению потоку "конец"... но сам обьект потока это не освобождает, для этого надо или явно Free сделать (подождав для гарантии завершения Execute (WaitFor)), или указать освобождать автоматом (FreeOnTerminate) что у меня и сделано.

> что при выходе из приложения метод Free не понадобится?
не понадобится, оно завершится и уничтожится гораздо раньше (меньше секунды после создания и запуска... но вообще возможны варинты ;)


 
sniknik ©   (2006-04-12 17:11) [37]

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


 
Сергей М. ©   (2006-04-12 17:16) [38]


> как убить тред в любой момент времени, чтобы от него ничего
> не осталось?
>


Собственно тред как ОС-объект "убивается" явным или неявным вызовом TerminateThread().



Страницы: 1 вся ветка

Форум: "Начинающим";
Текущий архив: 2006.04.30;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.56 MB
Время: 0.011 c
2-1144918680
Dzhin
2006-04-13 12:58
2006.04.30
Выкачивание файлов с интернета.


9-1128962305
JurMan
2005-10-10 20:38
2006.04.30
OnLine games


15-1144390810
Vlad Oshin
2006-04-07 10:20
2006.04.30
Переведите с московского на русский :)


15-1144404748
Vlad
2006-04-07 14:12
2006.04.30
Задачка из жизни.


2-1145010184
_Iton_
2006-04-14 14:23
2006.04.30
TreeView





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский