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

Вниз

Использование TTimer   Найти похожие ветки 

 
JaoDa   (2005-02-18 13:51) [0]

Копался в чужом коде и обнаружил такую строку:

TForm1.Timer1Timer(Sender: TObject);
begin  
 длительный процесс, превышающий время установленного интервала
end;

Обычно сам всегда делаю так:

TForm1.Timer1Timer(Sender: TObject);
begin  
 Timer1.Enabled:=false;
 длительный процесс, превышающий время установленного интервала
 Timer1.Enabled:=true;
end;

Как правильно? не ведет ли первый способ к множественной попытке выполнения одних и тех же вычислений?


 
Digitman ©   (2005-02-18 14:13) [1]


> не ведет ли


в контексте одного и того же трэда, реагирующего на сообщения им же созданногго таймера - не ведет.

во втором же способе Timer1.Enabled:=false/true - как телеге пятое колесо


 
Alexander Panov ©   (2005-02-18 14:22) [2]

Digitman ©   (18.02.05 14:13) [1]


procedure TForm1.Timer1Timer(Sender: TObject);
var
 i: Integer;
begin
 Memo1.Lines.Add(IntToStr(Tag));
 Tag := Tag+1;
 for i := 0 to 500 do
 begin
   Sleep(10);
   Application.ProcessMessages;
 end;
end;


JaoDa   (18.02.05 13:51)
Как правильно? не ведет ли первый способ к множественной попытке выполнения одних и тех же вычислений?


Ведет. Пример см. выше.


 
Alexander Panov ©   (2005-02-18 14:23) [3]

к [2] - интервал таймера - 1000


 
Digitman ©   (2005-02-18 14:30) [4]


> Alexander Panov ©   (18.02.05 14:22) [2]


> Ведет


никто, в т.ч. и автор, не ограничивал меня в праве подразумевать нечто свое под выражением "одни и те же вычисления"

ШТАТНОЕ (т.е. ЯВНО и ОСОЗНАННО предусмотренное программистом) временное прерывание хода некоего блока вычислений  в некоей точке ради потенциального выполнения того же блока вычислений (с возможно иными исх.параметрами), imho, никак нельзя назвать "ведущим к множественным попыткам" чего-то там ..


 
Alexander Panov ©   (2005-02-18 14:32) [5]

как я понял автора, вопрос у него был таков, что если код в обработчике таймера занимает промежуток времени, превышающий интервал, то не ведет ли такая ситуация к повторному вхождению в эту процедуру...

Вот я и привел иллючтрацию повторного входа в бработчик таймера в случае, если он не переводится в выключенное состояние.


 
BlackTr   (2005-02-18 14:36) [6]


> Вот я и привел иллючтрацию повторного входа в бработчик
> таймера в случае, если он не переводится в выключенное состояние.

Если убрать Application.ProcessMessages то не приведет к повторному входу


 
Alexander Panov ©   (2005-02-18 14:37) [7]

BlackTr   (18.02.05 14:36) [6]
Если убрать Application.ProcessMessages то не приведет к повторному входу


Приведет. Если увеличишь значительно цикл.


 
XP   (2005-02-18 14:39) [8]

2 Alexander Panov

Только настоящий индеец может такое написать...
Эдак завтра рекурсивные конструкции объявите вне закона:

procedure DoSomething;
begin
 if <SomeCondition> then
   DoSomething;
end;


Кто же в обработчике событий таймера использует ProcessMessages таким образом? И вообще, ProcessMessages - la move ton. А особенно в обработчиках событий таймера.
Не доводите идею до абсурда!
Уж раз грабельки подложили, так хоть бы соломки подстелили.

2 JaoDa
 При помещении сообщения от таймера в очередь - производится поиск сообщений от этого же таймера в этой же очереди. При наличии такого сообщения, новое сообщение не добавляется.

 А то, что написал Александр - пример того, как не надо делать.

P.S. Я все же надеюсь, что приведенный в [2] код - на самом деле приведен для того, чтобы показать, как не надо делать.


 
Alexander Panov ©   (2005-02-18 14:47) [9]

XP   (18.02.05 14:39) [8]
При помещении сообщения от таймера в очередь - производится поиск сообщений от этого же таймера в этой же очереди. При наличии такого сообщения, новое сообщение не добавляется.


Можно пояснить, откуда такая информация?

XP   (18.02.05 14:39) [8]
P.S. Я все же надеюсь, что приведенный в [2] код - на самом деле приведен для того, чтобы показать, как не надо делать.


Нет, не для этого, а для того, чтобы показать, что повторное вхождение в процедуру обработки неизбежно.

Моветон? - аргументы?


 
Sandman25 ©   (2005-02-18 14:49) [10]

Допустим, таймер поставлен на 4 секунды.
Допустим, обработка таймера занимает 1 секунду.
Если я не выключу таймер в начале обработки, следующая обработка начнется через 4 секунды после начала предыдущей обработки.
А если выключу, то через 5 секунд после начала предыдущей обработки. Так?
Или же сообщение таймеру убирается из очереди только после завершения обработки, и поэтому в любом случае пройдет 5 секунд после начала предыдущей обработки?


 
Digitman ©   (2005-02-18 14:51) [11]


> Alexander Panov ©   (18.02.05 14:37) [7]
> Приведет. Если увеличишь значительно цикл.


Александр, ну в индейцы тебя отнюдь не зря в этом случае записали)

ты о каких "циклах"-то ? выборки/дисп-ции/обработки сообщений ? или о каких ?


 
Alexander Panov ©   (2005-02-18 14:55) [12]

Digitman ©   (18.02.05 14:51) [11]
Про цикл неправильно выразился-)

Если в приведенном коде

procedure TForm1.Timer1Timer(Sender: TObject);
var
i: Integer;
begin
Memo1.Lines.Add(IntToStr(Tag));
Tag := Tag+1;
for i := 0 to 500 do
begin
  Sleep(10);
  Application.ProcessMessages;
end;
end;


заменить
for i := 0 to 500 do на for i := 0 to 5000000 do(Допустим) и убрать ProcessMessage, то в очереди сообщений за время работы for может появиться(и появится) и обработаться следующее сообщение от таймера, что приведет к повторному входу в обработчик.


 
XP   (2005-02-18 14:57) [13]

Можно пояснить, откуда такая информация?

Remarks
The DispatchMessage function forwards this message when no other messages are in the thread"s message queue.

WM_TIMER + F1

повторное вхождение в процедуру обработки неизбежно.

При таком написании кода - да, неизбежно.
Но я уже сказал, что это грабли поставленные самому себе.
Ибо Timer + ProcessMessages + Sleep = гремучая смесь как с точки зрения логики, так и с точки зрения алгоритмического подхода. Это уже не винегрет програмный, а какая-то окрошка.
Нет, я не спорю, можно и не такое написать. Но думать-то при этом надо о последствиях.

аргументы?
А какие есть у Вас аргументы против использования, например, GOTO? Или Вы им все же пользуетесь?


 
Alexander Panov ©   (2005-02-18 14:59) [14]

XP   (18.02.05 14:57) [13]
А какие есть у Вас аргументы против использования, например, GOTO?


У меня против GOTO нет аргументов.
Если мне нужно будет - буду использовать.


 
Alexander Panov ©   (2005-02-18 15:02) [15]

XP   (18.02.05 14:57) [13]
Remarks
The DispatchMessage function forwards this message when no other messages are in the thread"s message queue.

WM_TIMER + F1


Речь идет всего лишь о том, что сообщение таймера имеет более низкий приоритет перед друними сообщениями, и не более того.

Еще есть какая-либо информация?


 
BlackTr   (2005-02-18 15:09) [16]


> for i := 0 to 500 do на for i := 0 to 5000000 do(Допустим)
> и убрать ProcessMessage, то в очереди сообщений за время
> работы for может появиться(и появится)

Че-то не появилось. :))))


 
Alexander Panov ©   (2005-02-18 15:11) [17]

BlackTr   (18.02.05 15:09) [16]
Че-то не появилось. :))))


Ну значит, еще увеличь время работы for.
Смысл в том, что через некоторое время следующее сообщение из очереди сообщений все равно будет выбрано и обработано.


 
Digitman ©   (2005-02-18 15:12) [18]


> Alexander Panov ©   (18.02.05 15:02) [15]


ой ну ты в дебри полез) .. с эти WM_TIMER и его приоритетом ..

а завтра у автора вожжа под хвост с неким WM_ХРЕНАЙВЕР - тоже про "приоритеты" разглагольствовать будем с ним ?...


 
Alexander Panov ©   (2005-02-18 15:12) [19]

BlackTr   (18.02.05 15:09) [16]

и если это не случилось за достаточно короткий промежуток времени, это не значит, что этого не произойдет никогда.


 
BlackTr   (2005-02-18 15:13) [20]

[10]
> Если я не выключу таймер в начале обработки, следующая обработка
> начнется через 4 секунды после начала предыдущей обработки.
> А если выключу, то через 5 секунд после начала предыдущей
> обработки. Так?

Ага


 
Alexander Panov ©   (2005-02-18 15:13) [21]

Digitman ©   (18.02.05 15:12) [18]

Так это не автору. XP привел цитату из SDK. Это ему был экивок.


 
BlackTr   (2005-02-18 15:15) [22]


> Ну значит, еще увеличь время работы for.
> Смысл в том, что через некоторое время следующее сообщение
> из очереди сообщений все равно будет выбрано и обработано.

Оно придет когда ты выйдешь из обработчика Timer1Timer, увеличивай время работы for на скоко хош...


 
Alexander Panov ©   (2005-02-18 15:17) [23]

BlackTr   (18.02.05 15:15) [22]
Оно придет когда ты выйдешь из обработчика Timer1Timer,


Оно придет ровно по истечении интервала, заданного в TTimer.(плюс-минус, естественно).

Вот когда оно из очереди будет выбрано - другой вопрос. Но то, что будет выбрано - можешь быть уверен.


 
BlackTr   (2005-02-18 15:37) [24]


> Оно придет ровно по истечении интервала, заданного в TTimer.(плюс-минус,
> естественно).

Во время обработчика Timer1Timer оно не придет никогда, если только не ипользовать умную конструкцию Application.ProcessMessages. :))
Которая сама вызывает выборку сообщений из очереди...


 
XP   (2005-02-18 15:41) [25]

Оно придет ровно по истечении интервала, заданного в TTimer.(плюс-минус, естественно). Вот когда оно из очереди будет выбрано - другой вопросНо то, что будет выбрано - можешь быть уверен.

 
Timer.Interval := 100;
Timer1.Enabled := TRUE;
Sleep(5000);
Timer1.Enabled := FALSE;


В обработчике события таймера напишите что угодно, ну, хотя бы увеличивайте значение некоей переменной, и посмотрите, сколько раз оно выполнится.


 
Sandman25 ©   (2005-02-18 15:45) [26]

[20] BlackTr   (18.02.05 15:13)

Спасибо


 
Digitman ©   (2005-02-18 16:28) [27]


> BlackTr   (18.02.05 15:37) [24]


ой-ой..
а если "ждущий" трэд не есть осн.трэд ?


 
BlackTr   (2005-02-18 16:34) [28]

А об этом не спрашивали :)


 
Alexander Panov ©   (2005-02-18 16:43) [29]

XP   (18.02.05 15:41) [25]
Чуток поэкспериментирую, результат доложу-)


 
XP   (2005-02-18 18:06) [30]

2 Alexander Panov

Ага. Я тоже поэкспериментирую.
Хотя, я уже давно экспериментировал, и обнаружил, что сообщения от таймера в очереди не накапливаются.
Исчерпывающей документации по WM_TIMER я не нашел, и есть несколько "но".
Одно из них - из Help"а:

The KillTimer function does not remove WM_TIMER messages already posted to the message queue.

И это "но" говорит о том, что сообщения должны вроде бы как накапливаться. Но... не накапливаются. Что, в свою очередь, говорит о том, что в очередь дублирующие сообщения просто не добавляются.

Это практические наблюдения над поведением Windows Timer.

Именно это дает мне основание утверждать, что, если обработка события таймера по длительности превышает интервал срабатывания таймера, то никаких побочных эффектов (кроме увеличения интервала между вызовами метода обработки события) не наблюдается.
То-есть, никакого переполнения очереди, памяти, стека и прочих "чувствительных" к переполнению объектов.

P.S. А насчет недобавления повторного сообщения от таймера в очередь - так я это еще во времена Windows 3.1 слыхал. Правда, не помню от кого...


 
XP   (2005-02-18 18:17) [31]

Вот, нашел все-таки. :)

http://support.microsoft.com/default.aspx?scid=kb;en-us;78599

Timer event processing will not be interrupted by new timer events. This is because of the way that Windows notifies an application that a timer event has occurred. Instead of interrupting the application, Windows places a WM_TIMER message in its message queue. If there is already a WM_TIMER message in the queue from the same timer, the new message will be consolidated with the old one.

After the application has completed processing the current timer event, it checks its message queue for any new messages. This queue may have new WM_TIMER messages to process. There is no way to tell if any WM_TIMER messages have been consolidated.


 
Alexander Panov ©   (2005-02-18 18:23) [32]

XP   (18.02.05 18:17) [31]

Во!
Теперь то, что нужно-)
Спасибо за инфу!

Теперь надо разобраться, что же происходит, когда в обработчике появляется ProcessMessage...


 
XP   (2005-02-18 19:05) [33]

2 Alexander Panov

Пожалуйста. :)

Теперь надо разобраться, что же происходит, когда в обработчике появляется ProcessMessage

А происходит следующее.
В случае, когда в обработчике события от таймера нет вызова ProcessMessages, и даже если время обработки события многократно превышает интервал таймера, в очереди все равно хранится только одно сообщение.

После выхода из метода обработки события из очереди извлекается это сообщение, и снова вызывается метод обработки события таймера.

К слову, так как приоритет событий от таймера невысокий, то за этот период успевают обработаться практически все события, имеющиеся в очереди. Хотя, с приоритетом я могу путать. Но это к слову.

Таким образом, имеем метод обработки события таймера, после выхода из которого гарантированно иммеем в очереди сообщение от таймера.

Теперь рассмотрим вызов ProcessMessages внутри метода обработки события таймера.

Вызов ProcessMessages безусловный. По крайней мере, мы сейчас рассматриваем именно этот случай.

При вызове ProcessMessages происходит извлечение сообщения от таймера, и, как следствие - повторный вызов метода обработки события таймера.

При этом, и я на этом акцентирую внимание, происходит удаление сообщения от таймера из очереди. А при одном из очередных вызовов ProcessMessages, когда наступит время, нас опять будет ждать новое сообщение от таймера. Если бы мы не вызывали ProcessMessages, то имели бы в очереди одно единственное сообщение от таймера. А так - мы его обрабатываем, удаляем, а оно появляется вновь и вновь.

Так что же это за "повторный вызов метода обработки событий таймера"? Я подозреваю (а я очень в таких делах подозрителен), что это случай обыкновенной, хотя и завуалированной, рекурсии. Причем, рекурсии безусловной (так как ProcessMessages вызывается безусловно).

При коротком интервале срабатывания таймера и длительной обработке его сообщения - имеем постоянный вложенный вызов метода таймера. И, как следствие - медленное (хотя кто сказал, что медленное?) заполнение стека вплоть до его переполнения.

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

Резюмируя сказанное:

Во избежание переполнения стека, ProcessMessages внутри метода обработки события от таймера вызывать не следует. Даже если длительность выполнения метода значительно меньше интервала срабатывания таймера, не исключена ситуация, когда получим "наложение". Какая ситуация? Да любая! Длительность выполнения метода зависит от быстродействия процессора, приоритета основного потока приложения, занятости процессора и многого другого. В то время, как интервал срабатывания таймера остается (с некоторой натяжкой) постоянным. Вот вам и повод переполнить стек.
Все же, MS не зря сделал эту самую "consolidation" сообщений от таймера. Ибо, в противном случае могут быть проблемы с переполнением очереди сообщений, стеком и пр.

С уважением...


 
Alexander Panov ©   (2005-02-18 19:23) [34]

XP   (18.02.05 19:05) [33]

Абсолютно согласен.
Спасибо за развернутый анализ.



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

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

Наверх





Память: 0.56 MB
Время: 0.039 c
1-1108965730
Unknown
2005-02-21 09:02
2005.03.06
Возникло 3 вопроса:


1-1109016306
Wonderer
2005-02-21 23:05
2005.03.06
Товарищ Панов! Это что, не тема сообщения?


14-1108102012
MBo
2005-02-11 09:06
2005.03.06
Пятничная разминка заскорузлых нейронов и аксонов :)


3-1107862956
Mishenka
2005-02-08 14:42
2005.03.06
Как с помощью DBImage просмотреть gif ы из базы?


4-1105958331
IllusoryOrion
2005-01-17 13:38
2005.03.06
WM_NCLBUTTONUP





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