Форум: "Основная";
Текущий архив: 2004.10.31;
Скачать: [xml.tar.bz2];
ВнизРабота с потоками Найти похожие ветки
← →
Izyum © (2004-10-14 16:18) [0]Возникла задача создать многопоточное приложение...
Вторичный поток должен выполнять определенную работу и отображать результаты индикаторами на главной форме...
Но в момент, когда я беру мышом заголовок главной формы - вторичный поток тоже останавливается...
тот же эффект наблюдается в демке из стандартной поставки...
ВОПРОС: это так и должно быть или есть способ создать вторичный поток, который работает абсолютно независимо от главного?
← →
Суслик © (2004-10-14 16:19) [1]
> вторичный поток тоже останавливается...
как проверил?
← →
Izyum © (2004-10-14 16:22) [2]Во вторичном потоке делаю тупое наращивание переменной и вывожу ее значение в заголовок гланой формы...
Так вот, когда нажимаю кнопик мыша на заголовке - цифири перестают "бежать". Но если подержать секунд десять (должно уже заметно вперед продвинуться это сложное вычисление) и отпустить, цифири продолжают считать с той, на которой произошло нажатия кнопика...
ЗЫ: надеюсь понятно описал;)
← →
Суслик © (2004-10-14 16:26) [3]
> Во вторичном потоке делаю тупое наращивание переменной
> и вывожу ее значение в заголовок гланой формы...
Но это не значит, что поток остановился. Дело в том, что vcl принципиально онднозадачаная - только один поток может менять что-то в формах!
Читай и изучай метод tthread.synchronize
← →
Izyum © (2004-10-14 16:30) [4]Значит не полностью понятно объяснил...
Если на главной форме ничего не меняется из-за того, что я держу заголовок мышью - во вторичном потоке inc(i) должно работать?
Если нет - по чему?
Если да - не работает!
Дело в том, что за 10 секунд код inc(i) должен был достаточно много "накрутить". Правильно? Икогда я отпустил заголовок - обновления заголовка должно быб продолжиться с текущего значения i. Правильно? Так вот если при значении 1000 я нажал мыша, подержал 10 секунд и отпустил - в заголовке побежало начиная с 1001... Хотя я ожидал там увидеть, к примеру, 2001...
← →
Григорьев Антон © (2004-10-14 17:21) [5]В это время не работает Synchronize, т.к. он выполняется в главной нити, а главная нить занята обработкой перемещения формы. Если бы не было Synchronize, нить спокойно продолжала бы свою работу.
← →
Izyum © (2004-10-14 17:28) [6]Ага, хоть что-то проясняется...
Тогда может кто-то подскажет как реализовать такую задачу:
во вторичном потоке необходимо ГАРАНТИРОВАНО считывать данные из какого-либо порта (давайте для примера возьмем COM)и по возможности отображать результаты на главной форме. Если, к примеру, пользователь выполняет какие-либо действия над главной формой, которые не дают выполнится Synchronize - пусть интерфейс не обновляется, но данные из порта должны быть вычитаны все равно. реально как-то это реализовать?
← →
panov © (2004-10-14 17:32) [7]>Izyum © (14.10.04 17:28) [6]
Так не надо на каждый шаг выполнять обновление формы или визуальных объектов VCL.
В конце-концов, если такая необходимость есть, используй синхронизацию методом посылки сообщений из доп. потока в основной и обрабатывай их.
В этом случае доп. поток не будет ожидать обновления.
← →
Defunct © (2004-10-14 17:38) [8]Izyum © (14.10.04 17:28) [6]
> Тогда может кто-то подскажет как реализовать такую задачу:
во вторичном потоке необходимо ГАРАНТИРОВАНО считывать данные из какого-либо порта (давайте для примера возьмем COM)и по возможности отображать результаты на главной форме.
Создавать еще один поток для синхронизации вывода.
← →
Izyum © (2004-10-14 17:38) [9]Александр, спасибо за пинок в нужную сторону - я про эту возможность совсем позабыл:)
← →
Izyum © (2004-10-14 18:06) [10]Defunct © (14.10.04 17:38) [8]
А чуть более развернуто можно?:)
← →
Defunct © (2004-10-14 21:59) [11]> А чуть более развернуто можно?:)
Можно. Я сразу приведу пример.
Допустим есть критический к приостанову поток, который работает в 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;
← →
Defunct © (2004-10-14 22:12) [12][11]
TReportSyncThread взят из рабочего проекта. Там помимо вывода информации в Memo, предусмотрено выполнение TNotifyEvent и процедур работающих с визуальными компонентами (см. конструкторы). Таким образом вопросом синхронизации с основыным потоком занимается исключительно TReportSyncThread, ну а ваш поток работает без приостановок.
← →
Izyum © (2004-10-15 08:56) [13]Defunct © (14.10.04 22:12) [12]
Сенкс
← →
Erik1 © (2004-10-15 10:26) [14]А вобщето бери готорый компонент на Torry.net и немайся дурью. Тебе хороший коипонент еще несколько лет писать. Смотри чтобы подерживал overlaped.
← →
Izyum © (2004-10-15 11:22) [15]Erik1 © (15.10.04 10:26) [14]
Я не COM-порт слушать собираюсь... Нет у меня возможности использовать SetComMask... так что в моем случае overlaped не пригодится...
Если ошибаюсь - поправте, плиз...
Или WaitForSingleObject можно использовать при работе с ЛЮБЫМ портом?
← →
Zelius © (2004-10-15 11:48) [16]Поток для вывода данных помоему лишний, спокойно без него можно обойтись. Проще как Александр сказал, просто посылать сообщение главной форме о том что данные изменились, и пусть она сама лезет и смотрит, что там изменилось.
← →
Erik1 © (2004-10-15 12:04) [17]"Или WaitForSingleObject можно использовать при работе с ЛЮБЫМ портом?" Да именно с любым! И еще SetComMask никак несвязана с overlaped. Использование overlaped предпологает наличие аснихроного режима работы без нагрузки на процесор. По com порту могбы дать конкретные рекомендации или готовый компонент. Если чтото другое то что за порт если не секрет(надеюсь не IO)?
← →
Izyum © (2004-10-15 12:27) [18]Ну это уже детали...
Мне нужно в общем-то опрашивать порт 123h и при изменении данных в нем гарантировано записать что-то при выполнении определнного условия в порт 456h, и паралельно отобразить это на экране, если таковая возможность в данный момент времени имеется...
При такой постановке вопроса нужно использовать ПОСТОЯННЫЙ (в цикле) опрос порта, или можно как-то использовать WaitForSingleObject???
← →
Izyum © (2004-10-15 14:33) [19]Erik1 © (15.10.04 12:04) [17]
>>Если чтото другое то что за порт если не секрет(надеюсь не IO)?
Совершенно верно - IO:)))
← →
Defunct © (2004-10-15 20:04) [20]Zelius © (15.10.04 11:48) [16]
Завсит от стадии "готовности" программы и от того как спроектирована программа. Если функции вывода уже готовы, то лучше использовать доп. поток (меньше изменений в программе).
← →
Izyum © (2004-10-18 09:10) [21]Такс, передача результатов через сообщения помогло - вторичный поток не засыпает... Но тут вылезли другие проблемы: если главную форму взять за заголовок и достаточно долго подержать мышкой - приложение начисто зависает... При попытке снять его через три волшебных кнопки выдается сообщение "Системе существенно не хватает ресурсов"... Я так понял - очередь сообщения не резиновая и сколько угодно в нее не поместится необработанных сообщений...
С этим как-то бороться можно или надо изначально по другому пути идти?
← →
panov © (2004-10-18 10:06) [22]>Izyum © (18.10.04 09:10) [21]
Не надо столько сообщений много посылать.
Кстати, сообщения обрабатываются, даже если "...взять за заголовок и достаточно долго подержать мышкой..."
← →
Erik1 © (2004-10-18 10:07) [23]Если работаеш с 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;
Это общая заготовка.
← →
Erik1 © (2004-10-18 10:14) [24]А в InternalExec свой цикл делай, только добавь в него Sleep(5) или сколько ты можеш себе позволить. Можно эксперементально определить с какой минимальной частотой у тебя изменяются данные. Также добавь проверку на состояние события.
По приходу данных из порта ненадо их сразу окну отпралять, наверное генерируется огромное число сообщений. Тут разяснение требуется, что это за данные и для чего они нужны.
← →
Izyum © (2004-10-18 11:58) [25]Erik1 © (18.10.04 10:14) [24]
Спасибки, попробую разобраться с кодом...
Вторичный поток должен "слушать" порт 376h, данные в котором могут меняться ОЧЕНЬ интенсивно в один момент, а в другой быть совершенно неизменными...
Но, похоже, я изначально не тем путем пошел...
ЗЫ: Вышеприведенный код как раз и является реализацией использования WaitForMultipleObjects при мониторинге ЛЮБОГО порта?
← →
Erik1 © (2004-10-18 14:29) [26]Нет, в даном случае WaitForMultipleObjects тлько для ожидания потоком команды на выполнение и остановку. Для IO портов невозможно использование WaitFor функций.
Похоже, что действительно не тем путем. Если через порт 376h поступают данные, то надо записывать их в кукой то буфер и по завершению активности посылать сообшение. Еще можно записавать в PIPE а в гланом потоке проверять переменую указывающею размер буфера.
Но все это както странно, может ты обрабатываеш какоето прерывание? Вобще програмирование на апаратном уровне сильно отличается.
← →
Izyum © (2004-10-18 14:34) [27]Нет, это не прерывания... Было бы наверно проще с ними иметь дело, но не подходят они... 376h - альтернативный регистр состояния НЖМД второго канала...
В принципе я уже начал "копать" другой путь решения...
Но все равно, ВСЕМ спасибо
Страницы: 1 вся ветка
Форум: "Основная";
Текущий архив: 2004.10.31;
Скачать: [xml.tar.bz2];
Память: 0.54 MB
Время: 0.035 c