Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 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
14-1097289980
Думкин
2004-10-09 06:46
2004.10.31
С днем рождения! 9 октября


10-1056018991
landy
2003-06-19 14:36
2004.10.31
Вызов метода сервера через SocketConnection


1-1098171808
AntonSh
2004-10-19 11:43
2004.10.31
Архивация строки


14-1096896970
ArchValentin
2004-10-04 17:36
2004.10.31
Программа управления комп. салоном...


14-1097261055
123
2004-10-08 22:44
2004.10.31
Администраторам!





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