![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Работа с потоками [D6, Win95/98, WinXP]
Izyum © (2004-10-14 16:18:00)Возникла задача создать многопоточное приложение...
Вторичный поток должен выполнять определенную работу и отображать результаты индикаторами на главной форме...
Но в момент, когда я беру мышом заголовок главной формы - вторичный поток тоже останавливается...
тот же эффект наблюдается в демке из стандартной поставки...
ВОПРОС: это так и должно быть или есть способ создать вторичный поток, который работает абсолютно независимо от главного?
Izyum (2004-10-18 14:34:00) [1]Нет, это не прерывания... Было бы наверно проще с ними иметь дело, но не подходят они... 376h - альтернативный регистр состояния НЖМД второго канала...
В принципе я уже начал "копать" другой путь решения...
Но все равно, ВСЕМ спасибо
Erik1 (2004-10-18 14:29:00) [2]Нет, в даном случае WaitForMultipleObjects тлько для ожидания потоком команды на выполнение и остановку. Для IO портов невозможно использование WaitFor функций.
Похоже, что действительно не тем путем. Если через порт 376h поступают данные, то надо записывать их в кукой то буфер и по завершению активности посылать сообшение. Еще можно записавать в PIPE а в гланом потоке проверять переменую указывающею размер буфера.
Но все это както странно, может ты обрабатываеш какоето прерывание? Вобще програмирование на апаратном уровне сильно отличается.
Izyum (2004-10-18 11:58:00) [3]Erik1 © (18.10.04 10:14) [24]
Спасибки, попробую разобраться с кодом...
Вторичный поток должен "слушать" порт 376h, данные в котором могут меняться ОЧЕНЬ интенсивно в один момент, а в другой быть совершенно неизменными...
Но, похоже, я изначально не тем путем пошел...
ЗЫ: Вышеприведенный код как раз и является реализацией использования WaitForMultipleObjects при мониторинге ЛЮБОГО порта?
Erik1 (2004-10-18 10:14:00) [4]А в InternalExec свой цикл делай, только добавь в него Sleep(5) или сколько ты можеш себе позволить. Можно эксперементально определить с какой минимальной частотой у тебя изменяются данные. Также добавь проверку на состояние события.
По приходу данных из порта ненадо их сразу окну отпралять, наверное генерируется огромное число сообщений. Тут разяснение требуется, что это за данные и для чего они нужны.
Erik1 (2004-10-18 10:07:00) [5]Если работаеш с IO то конечно все самому придется делать. Использовать можно так:
type
TActiveEvent = (actExit, actSend);
RAction = record
Event: array[TActiveEvent] of THandle;
Call: array[TActiveEvent] of TNotifyEvent;
end;
PAction = ^RAction;
...
procedure TCustomThread.CreateEvent;
var
i: TActiveEvent;
begin
for i := Low(tmEvent.Event) to High(tmEvent.Event) do
begin
tmEvent.Event[i] := Windows.CreateEvent(nil, false, false, nil);
end;
end;
constructor TCustomThread.Create(TimeOut: Integer);
begin
inherited Create(True);
fTimeOut := INFINITE;
fID := Random(MaxInt);
fActive := False;
tmEvent.Call[actSend] := InternalExec;
tmEvent.Call[actExit] := BreakThread;
CreateEvent;
end;
procedure TCustomThread.Execute;
var
Status: DWord;
label
Retry;
begin
Retry:
Status := WaitForMultipleObjects(Ord(High(tmEvent.Event)) + 1, @tmEvent.Event,
false, fTimeOut); //INFINITE
if fActive and (TActiveEvent(Status) <> actExit) then
exit;
fActive := True;
try
try
case Status of
Ord(Low(TActiveEvent))..Ord(High(TActiveEvent)):
begin
tmEvent.Call[TActiveEvent(Status)](Self);
if (TActiveEvent(Status) <> actExit) then
Synchronize(DoFinished);
end;
else
if Status = WAIT_TIMEOUT then
WaitTimeOut;
end;
except
InternalException;
end;
finally
FActive := False;
end;
if not Terminated then
goto Retry;
end;
Это общая заготовка.
panov (2004-10-18 10:06:00) [6]>Izyum © (18.10.04 09:10) [21]
Не надо столько сообщений много посылать.
Кстати, сообщения обрабатываются, даже если "...взять за заголовок и достаточно долго подержать мышкой..."
Izyum (2004-10-18 09:10:00) [7]Такс, передача результатов через сообщения помогло - вторичный поток не засыпает... Но тут вылезли другие проблемы: если главную форму взять за заголовок и достаточно долго подержать мышкой - приложение начисто зависает... При попытке снять его через три волшебных кнопки выдается сообщение "Системе существенно не хватает ресурсов"... Я так понял - очередь сообщения не резиновая и сколько угодно в нее не поместится необработанных сообщений...
С этим как-то бороться можно или надо изначально по другому пути идти?
Defunct (2004-10-15 20:04:00) [8]Zelius © (15.10.04 11:48) [16]
Завсит от стадии "готовности" программы и от того как спроектирована программа. Если функции вывода уже готовы, то лучше использовать доп. поток (меньше изменений в программе).
Izyum (2004-10-15 14:33:00) [9]Erik1 © (15.10.04 12:04) [17]
>>Если чтото другое то что за порт если не секрет(надеюсь не IO)?
Совершенно верно - IO:)))
Izyum (2004-10-15 12:27:00) [10]Ну это уже детали...
Мне нужно в общем-то опрашивать порт 123h и при изменении данных в нем гарантировано записать что-то при выполнении определнного условия в порт 456h, и паралельно отобразить это на экране, если таковая возможность в данный момент времени имеется...
При такой постановке вопроса нужно использовать ПОСТОЯННЫЙ (в цикле) опрос порта, или можно как-то использовать WaitForSingleObject???
Erik1 (2004-10-15 12:04:00) [11]"Или WaitForSingleObject можно использовать при работе с ЛЮБЫМ портом?" Да именно с любым! И еще SetComMask никак несвязана с overlaped. Использование overlaped предпологает наличие аснихроного режима работы без нагрузки на процесор. По com порту могбы дать конкретные рекомендации или готовый компонент. Если чтото другое то что за порт если не секрет(надеюсь не IO)?
Zelius (2004-10-15 11:48:00) [12]Поток для вывода данных помоему лишний, спокойно без него можно обойтись. Проще как Александр сказал, просто посылать сообщение главной форме о том что данные изменились, и пусть она сама лезет и смотрит, что там изменилось.
Izyum (2004-10-15 11:22:00) [13]Erik1 © (15.10.04 10:26) [14]
Я не COM-порт слушать собираюсь... Нет у меня возможности использовать SetComMask... так что в моем случае overlaped не пригодится...
Если ошибаюсь - поправте, плиз...
Или WaitForSingleObject можно использовать при работе с ЛЮБЫМ портом?
Erik1 (2004-10-15 10:26:00) [14]А вобщето бери готорый компонент на Torry.net и немайся дурью. Тебе хороший коипонент еще несколько лет писать. Смотри чтобы подерживал overlaped.
Izyum (2004-10-15 08:56:00) [15]Defunct © (14.10.04 22:12) [12]
Сенкс
Defunct (2004-10-14 22:12:00) [16][11]
TReportSyncThread взят из рабочего проекта. Там помимо вывода информации в Memo, предусмотрено выполнение TNotifyEvent и процедур работающих с визуальными компонентами (см. конструкторы). Таким образом вопросом синхронизации с основыным потоком занимается исключительно TReportSyncThread, ну а ваш поток работает без приостановок.
Defunct (2004-10-14 21:59:00) [17]> А чуть более развернуто можно?:)
Можно. Я сразу приведу пример.
Допустим есть критический к приостанову поток, который работает в RealTime (RT) и не должен пропустить ни одной порции данных поступающей на порт X. В то же время требуется выводить какую-то информацию для отчета в Memo. Реализация: создается доп. поток, задачей которого будет вывод в какой-то строки в Memo. После вывода этот поток автоматически удаляется:
TJob = Procedure of object;
TReportJob = Procedure(Msg: String) of object;
TReportSyncThread = class(TThread)
Private
FJob : TJob;
FReportJob : TReportJob;
FMsg : String;
FSender : TObject;
FNotifyEvent : TNotifyEvent;
Procedure DoReport;
Procedure DoJob;
Procedure DoNotifyEvent;
Protected
procedure Execute; override;
Public
Constructor Create(AJob:TJob);
Constructor CreateNotifyEvent(Sender:TObject; ANotifyEvent:TNotifyEvent);
Constructor PostMessage(AMsg:String;AReportJob:TReportJob);
End;
{******************************************}
{********** Report Sync Thread ************}
{******************************************}
Constructor TReportSyncThread.Create;
Begin
If Assigned(AJob) Then
Begin
FJob := AJob;
FreeOnTerminate := True;
Inherited Create(False);
Priority := tpNormal;
End;
End;
Constructor TReportSyncThread.CreateNotifyEvent;
Begin
If Assigned(ANotifyEvent) Then
Begin
FNotifyEvent := ANotifyEvent;
FSender := Sender;
FreeOnTerminate := True;
Inherited Create(False);
Priority := tpNormal;
End;
End;
Constructor TReportSyncThread.PostMessage;
Begin
If Assigned(AReportJob) Then
Begin
FReportJob := AReportJob;
FMsg := AMsg;
FreeOnTerminate := True;
Inherited Create(False);
Priority := tpNormal;
End;
End;
Procedure TReportSyncThread.DoReport;
Begin
If Assigned( FReportJob ) Then FReportJob(FMsg);
End;
Procedure TReportSyncThread.DoJob;
Begin
If Assigned(FJob) Then FJob;
End;
Procedure TReportSyncThread.DoNotifyEvent;
Begin
If Assigned(FNotifyEvent) Then FNotifyEvent(FSender);
End;
Procedure TReportSyncThread.Execute;
Begin
Synchronize(DoJob);
Synchronize(DoReport);
Synchronize(DoNotifyEvent);
End;
{********************************************}
Вывод информации в вашем потоке можно сделать например так:
Procedure TMyThread.SyncReport(Msg:String);
Begin
If Assigned(FMemo) Then
Try
FMemo.Lines.Add(Msg);
Except
End;
End;
Procedure TMyThread.Report(Msg: String);
Begin
If Not Terminated Then
TReportSyncThread.PostMessage(Msg, SyncReport);
End;
Procedure TMyThread.Execute;
Var I:Integer;
Begin
I := 0;
While not Terminated Do
Begin
// Ждем данных с порта
Inc(i);
Report('Прочитали порцию '+IntToStr(I)); // Вывод какой-то
инфы
End;
End;
Izyum (2004-10-14 18:06:00) [18]Defunct © (14.10.04 17:38) [8]
А чуть более развернуто можно?:)
Izyum (2004-10-14 17:38:00) [19]Александр, спасибо за пинок в нужную сторону - я про эту возможность совсем позабыл:)
Defunct (2004-10-14 17:38:00) [20]Izyum © (14.10.04 17:28) [6]
> Тогда может кто-то подскажет как реализовать такую задачу:
во вторичном потоке необходимо ГАРАНТИРОВАНО считывать данные из какого-либо порта (давайте для примера возьмем COM)и по возможности отображать результаты на главной форме.
Создавать еще один поток для синхронизации вывода.