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

Вниз

Правильно "заморозить" TThread   Найти похожие ветки 

 
Alex_C   (2009-11-23 09:57) [0]

Какой командой правильно (в смысле чтоб не грузила основной процесс) приостановить на некоторое время выполнение TThread? Я пользовался Sleep, но вот возникли некоторые подзрения, что это не совсем верно :)


 
Leonid Troyanovsky ©   (2009-11-23 10:01) [1]


> Alex_C   (23.11.09 09:57)
 
> Какой командой правильно (в смысле чтоб не грузила основной
> процесс) приостановить на некоторое время выполнение TThread?

Зависит от цели.

--
Regards, LVT.


 
Сергей М. ©   (2009-11-23 10:02) [2]

Почему не верно ?
Вполне нормальный способ, имеющий право на жизнь наравне с [Msg]WaitFor-функциями


 
Alex_C   (2009-11-23 10:14) [3]


> Зависит от цели.


Цель тут очень простая - надо через равные промежутки времени в ком-порт посылать комманду.
Делаю так:


procedure TGetThread.Execute;
begin
 FreeOnTerminate := True;
 while not Terminated do
 begin
   Sleep(PoolInterval);
   WriteToCOMPort;
 end;


 
Сергей М. ©   (2009-11-23 10:22) [4]

В период "сна" PoolInterval поток не реагирует ни на что.
Ты это осознаешь ?


 
Leonid Troyanovsky ©   (2009-11-23 10:28) [5]


> Alex_C   (23.11.09 10:14) [3]

> Цель тут очень простая - надо через равные промежутки времени
> в ком-порт посылать комманду.

MS Windows не гарантирует ни равномерность, ни отсутствие пропусков,
бо, не является RTOS (exclude CE).

--
Regards, LVT.


 
Alex_C   (2009-11-23 10:33) [6]


> В период "сна" PoolInterval поток не реагирует ни на что.


Вот!
Осознаю! :) И хочу чтоб на момент "сна" он реагировал на Terminate!
Как правильно? WaitForSingleObject? А что тогда ставить первым параметром?


 
Alex_C   (2009-11-23 10:44) [7]

Так будет верно?


procedure TGetThread.Execute;
begin
 FreeOnTerminate := True;
 while not Terminated do
 begin
   WaitForSingleObject(GetThread.Handle, FOwner.PoolInterval);
   WriteToCOMPort;
end;


 
MBo ©   (2009-11-23 10:50) [8]

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


 
Сергей М. ©   (2009-11-23 10:56) [9]


> Alex_C   (23.11.09 10:44) [7]


Можно поступить так: ждать заданное время с пом. MsgWaitForMultipleObjects, реагируя при этом на сообщения, адресованные потоку.
Другой поток, который желает терминировать данный поток, посылает (PostThreadMessage) ему некое предопределенное сообщение, при получении которого данный поток взводит свой же флаг Terminated, который анализируется в while-условии с целью выхода из цикла.


 
Сергей М. ©   (2009-11-23 10:58) [10]


> Alex_C   (23.11.09 10:44) [7]


Можно и WaitForSingleObject использовать, но при этом придется создавать какой-либо объект синхронизации (например, event или mutex), который будет "взводиться" другим потоком.


 
Alex_C   (2009-11-23 11:36) [11]

Прошу прощения, но для меня сообщения WaitFor... - камень предкновения!
Вроде все и просто, а как правильно их использовать - до концаразобраться не могу.
По этому еще раз спрошу:
1. для того чтоб завершить нить TGetThread, я из основной программы  пошлю

PostThreadMessage(GetThread.Handle, WM_QUIT, 0, 0);

2. а вот в цикле самой нити как правильно делать?

procedure TGetThread.Execute;
begin
 FreeOnTerminate := True;
 while not Terminated do
 begin
   if MsgWaitForMultipleObjects(1, ???, False, FOwner.PoolInterval, QS_ALLINPUT)
     <> WAIT_TIMEOUT then
   begin
     Terminate := True;
     Break;
   end;
...


Вторым параметром что указывать?


 
Сергей М. ©   (2009-11-23 11:48) [12]


> из основной программы  пошлю
>
> PostThreadMessage(GetThread.Handle


PostThreadMessage(GetThread.ThreadId, ..)

if MsgWaitForMultipleObjects(1, Handle, False, FOwner.PoolInterval, QS_ALLINPUT) = WAIT_OBJECT_0 + 1 then
 begin
   if PeekMessage(Msg, 0, WM_QUIT, WM_QUIT, PM_REMOVE) then
     Terminated := True;
 end
else
..


 
Вариант   (2009-11-23 11:53) [13]

Возможный вариант -

procedure EndThread(P: DWORD); stdcall;
begin
// А ничего делать и не надо
end;

procedure TGetThread.Execute;
begin
FreeOnTerminate := True;
while not Terminated do
begin
    SendToPort();// Что-то послали в порт
// Ждем или окончания интервала или прерывания потока
   SleepEx(PoolInterval,true);
end;

//ПРоцедура остановки потока, переменная Th - объект класса TGetThread
procedure TForm1.OnButton1Click(Sender:TObject);
begin
    if Th <> nil then
   begin
       Th.Terminate;        
      //QueueUserAPC выводит поток из состояния спячки
       if not QueueUserAPC(func, Th.Handle, 0) then
       begin
         err := GetLastError;
         ShowMessage("QueueUserAPC Error - " + SysErrorMessage(err));
       end;
  end;
end;


 
Вариант   (2009-11-23 11:59) [14]

Корректировка
//ПРоцедура остановки потока, переменная Th - объект класса TGetThread
procedure TForm1.OnButton1Click(Sender:TObject);
begin
   if Th <> nil then
  begin
      Th.Terminate;        
     //QueueUserAPC выводит поток из состояния спячки
      if not QueueUserAPC(@EndThread, Th.Handle, 0) then
      begin
        err := GetLastError;
        ShowMessage("QueueUserAPC Error - " + SysErrorMessage(err));
      end;
 end;
end;


 
Separator ©   (2009-11-23 12:03) [15]

Я пользуюсь наследниками от написанного класса, вот исходники модуля:
unit dbsLoopThread;

interface

uses
// Системные модули
 Windows, Classes;

type
 TdbsCustomLoopThread = class(TThread)
 private
   fEvent: THandle;
   fInterval: Cardinal;
 public
   constructor Create(const aCreateSuspended: Boolean = False);
   destructor Destroy; override;

   property Interval: Cardinal read fInterval write fInterval;
   property
   procedure Loop; virtual; abstract;

   procedure Terminate; override;
   procedure Execute; override;
 end;

implementation

{ TdbsLoopThread }

constructor TdbsCustomLoopThread.Create(const aCreateSuspended: Boolean = False);
begin
 inherited Create(True);

 fInterval := 1000; // 1 секунда
 CreateEvent(nil, False, False, nil);

 if not aCreateSuspended then
   Resume;
end;

destructor TdbsCustomLoopThread.Destroy;
begin

 inherited Destroy;
end;

procedure TdbsCustomLoopThread.Terminate;
begin
 inherited Terminate;
 SetEvent(fEvent);
end;

procedure Execute;
begin
 if not Terminated then
 repeat
   Work;
 until (Terminated) or (WaitForSingleObject(fEvent, fInterval) <> WAIT_TIMEOUT);
end;

end.


Кстати, мастера посмотрите, может чего не правильно работает?


 
Separator ©   (2009-11-23 12:05) [16]

Извините, не тот деструктор написал:
destructor TdbsCustomLoopThread.Destroy;
begin
 if not Terminated then
 begin
   Terminate;
   if Suspended then
     Resume;
 end;
 WaitFor;
 CloseHandle(fEvent);
 inherited Destroy;
end;


 
RWolf ©   (2009-11-23 12:06) [17]


> CreateEvent

TEvent.Create, как вариант.


 
Alex_C   (2009-11-23 12:14) [18]


> Вариант

Этот вариант решения задачи я вообще незнал - надо будет его более подробно изучить...


> Сергей М.


Yes! Все заработало!

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


procedure TGetThread.Execute;
var
 Handles: array[0..0] of THandle;
 Msg: TMsg;
begin
 FreeOnTerminate := True;
 Handles[0] := GetThread.Handle;
 while true do
 begin
   if MsgWaitForMultipleObjects(1, Handles, False, FOwner.PoolInterval, QS_ALLINPUT) = WAIT_OBJECT_0 + 1 then
   begin
     if PeekMessage(Msg, 0, WM_QUIT, WM_QUIT, PM_REMOVE) then
       Break;
   end
   else
   begin
   ...тут выполняется какая то задача треда
  end
end.


чтобы остановить поток из основного потока посылаем сообщение

PostThreadMessage(GetThread.ThreadID, WM_QUIT, 0, 0);


Вроде все так - по крайней меня у меня все заработало!


 
Leonid Troyanovsky ©   (2009-11-23 12:28) [19]


> Вариант   (23.11.09 11:53) [13]

Т.к. Terminate не виртуальный, то придется и деструктор override.
QUACP можно в Terminate перенести, а если вместо SleepEx
использовать WF..OEx, то можно и причину узнать, что был ACP.

Во-щем, направление правильное, а над деталями еще
можно поработать.

--
Regards, LVT.


 
Leonid Troyanovsky ©   (2009-11-23 12:39) [20]


> Separator ©   (23.11.09 12:03) [15]

> Кстати, мастера посмотрите, может чего не правильно работает?

У WFSO есть еще  WAIT_FAILED.

> WaitFor;
>  CloseHandle(fEvent);
>  inherited Destroy;

inherited Destroy сам делает WaitFor.

--
Regards, LVT.


 
Separator ©   (2009-11-23 12:41) [21]

Так мне тут WaitFor обязательно нужен, чтобы не закрывать fEvent до выхода из Execute, хотя в принципе тогда просто вывалиться в WAIT_FAILED


 
Вариант   (2009-11-23 12:47) [22]


> Leonid Troyanovsky ©   (23.11.09 12:28) [19]


> то можно и причину узнать, что был ACP.

SleepEx тоже возвращает WAIT_IO_COMPLETION,
но согласен,  обычно задачи сложнее и как правило надо ждать еще какие-либо события (
> WF..OEx

)


 
Leonid Troyanovsky ©   (2009-11-23 12:48) [23]


> Alex_C   (23.11.09 12:14) [18]

>  Handles[0] := GetThread.Handle;
>  while true do
>  begin
>    if MsgWaitForMultipleObjects(1, Handles

Зачем ему ждать собс-ный хендл - не очень понятно.

> Вроде все так - по крайней меня у меня все заработало!

Вариант-то не очень, поток стал из рабочего гуевым.
Кста, QS_ALLINPUT - изишен, кроме QS_POSTMESSAGE
он ничего и не увидит.

--
Regards, LVT.


 
Сергей М. ©   (2009-11-23 12:56) [24]


> Зачем ему ждать собс-ный хендл - не очень понятно


Ну что-то же подставить нужно в кач-ве хэндла объекта синхронизации..

За неимением или ненадобностью каких-либо ивентов, мьютексов и иже с ними собственный хендл для этого вполне подойдет


 
Демо ©   (2009-11-23 13:21) [25]


> Alex_C   (23.11.09 12:14) [18]


Для полной корректности (в будущем может сыграть свою роль) желательно очередь обработки сообщений создать до того, как с ней начнётся работа:

procedure TGetThread.Execute;
var
 Handles: array of THandle;
 M: TMsg;
begin
 FreeOnTerminate := True;
 PeekMessage(M,0,0,0,PM_NOREMOVE);
 while MsgWaitForMultipleObjects(0,Handles,False,1000,QS_ALLINPUT)<>WAIT_OBJECT_0+1 do
 begin
   PeekMEssage(M,0,0,0,PM_REMOVE);
   if M.message=WM_QUIT then Break;
   //...
 end;


 
Leonid Troyanovsky ©   (2009-11-23 13:24) [26]


> Separator ©   (23.11.09 12:41) [21]

> fEvent до выхода из Execute, хотя в принципе тогда просто
> вывалиться в WAIT_FAILED

Тогда отставить, бо при CloseHandle может случится

If this handle is closed while the wait is still pending, the function"s behavior
is undefined.

--
Regards, LVT.



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

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

Наверх





Память: 0.52 MB
Время: 0.006 c
11-1210479486
Valera
2008-05-11 08:18
2010.01.17
Как загрузить иконку в Bitmap.


15-1258448018
KilkennyCat
2009-11-17 11:53
2010.01.17
Из Магадана в Москву или Петербург


2-1259073148
guest2009
2009-11-24 17:32
2010.01.17
ComboBox обрезка длинного текста


15-1258526556
ZeroDivide
2009-11-18 09:42
2010.01.17
Исключить упоминание о боге из гимна


2-1259491214
Drowsy
2009-11-29 13:40
2010.01.17
BPL и ошибки компиляции.





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский