Форум: "Начинающим";
Текущий архив: 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
end;
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;
← →
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