Форум: "Начинающим";
Текущий архив: 2008.08.03;
Скачать: [xml.tar.bz2];
ВнизЗагоны с таймером Найти похожие ветки
← →
Loginov Dmitry © (2008-07-02 23:50) [0]Может кто сталкивался.
Не то, чтобы проблему нельзя было решить, но не понимаю, какого хр. она вообще возникает (либо не возникает)
Есть обработчик таймера. Для упрощения содержимое следующее:
procedure TMyForm.MyTimerTimer(Sender: TObject);
begin
ALog.LogStr("before MyTimerTimer");
MyTimer.Enabled := False;
Sleep(10); // реально здесь выполняется ряд "полезных" действий
ALog.LogStr("after MyTimerTimer");
end;
по умолчанию MyTimer.Enabled=False
MyTimer.Interval=50
В событии FormShow на ряду с прочими "полезными" действиями осуществляется вызов MyTimer.Enabled := True;
Собственно все. В логе при этом вижу следующее:
before MyTimerTimer
after MyTimerTimer
before MyTimerTimer
after MyTimerTimer
before MyTimerTimer
after MyTimerTimer
т.е. таймер вместо одного раза срабатывает 3 раза.
С этим столкнулся на работе. Дома ситуация не воспроизводится.
Компилю на Delphi2007 SP2 с пакетами.
Мое мнение насчет причины данной проблемы такое: В событии FormShow после вызова MyTimer.Enabled := True продолжается инициализация, вешающая основной поток некоторое время, в течение которого в очередь окна таймера успевает поступить 3 сообщения WM_TIMER, и установка MyTimer.Enabled=False не в силах повлиять на это. Однако пытаюсь эмулировать задержку в FormShow дома (Delphi7) - никаких проблем не возникает. Как ни бьюсь, OnTime вызывается только один раз. Завтра разбирусь с этим вопросом, однако может кто-нибудь с этим уже разбирался, где правда?
← →
McSimm © (2008-07-03 00:07) [1]А FormShow точно один раз только вызывается ?
← →
Loginov Dmitry © (2008-07-03 00:20) [2]В принципе
The KillTimer function does not remove WM_TIMER messages already posted to the message queue.
Но все равно эмуляция данного "эффекта" на чистом проекте не получается :(
Делаю следующее:procedure TForm1.FormShow(Sender: TObject);
begin
MyTimer.Enabled := True;
sleep(1000);
end;
Ловится только одно WM_TIMER, хотя за 1 секунду их должно было накопиться 20 штук. Видимо одновременно в очереди сообщений не может быть двух WM_TIMER для одного и того же таймера. А работе наверно именно это и происходит.
Бред какой-то :(
← →
Loginov Dmitry © (2008-07-03 00:21) [3]> А FormShow точно один раз только вызывается ?
С этого-то я и начал со всем этим разбираться. FormShow вызывается только 1 раз.
← →
MsGuns © (2008-07-03 00:29) [4]У Вас, ув.Дмитрий, просто таки талант даже для самых простых задач искать и находить головоломные решения ;)
← →
Loginov Dmitry © (2008-07-03 00:32) [5]> Видимо одновременно в очереди сообщений не может быть двух
> WM_TIMER для одного и того же таймера.
Или их отсеивает DispatchMessage (куды они еще могут деваться?)
← →
Loginov Dmitry © (2008-07-03 00:35) [6]> У Вас, ув.Дмитрий, просто таки талант даже для самых простых
> задач искать и находить головоломные решения ;)
Обойти проблему - нужно несколько секунд. Но хотелось бы понять причину, чтоб впредь на подобном не спотыкаться.
← →
Anatoly Podgoretsky © (2008-07-03 00:43) [7]> Loginov Dmitry (02.07.2008 23:50:00) [0]
ALog.LogStr("before MyTimerTimer");
MyTimer.Enabled := False;
Местами менять не пробовал.
← →
Loginov Dmitry © (2008-07-03 00:47) [8]> Или их отсеивает DispatchMessage
Все-таки в MSDN пишут точнее:Timers expire at regular intervals, but a timer that expires multiple times before being serviced does not generate multiple WM_TIMER messages.
← →
Loginov Dmitry © (2008-07-03 00:49) [9]> Местами менять не пробовал.
А что должно измениться? В лог оно так и так запишет, никуда не денется.
← →
McSimm © (2008-07-03 00:50) [10]
> по умолчанию MyTimer.Enabled=False
Я бы еще это проверил, не визуально, а прямо перед установкой в true в обработчике.
← →
Anatoly Podgoretsky © (2008-07-03 00:52) [11]> Loginov Dmitry (03.07.2008 0:49:09) [9]
Измениться может много, ведь содержимое процедуры неизвестно, возможно пока она работает успевает прийти еще одно сообщение, два не может Виндоус не позволит.
← →
Loginov Dmitry © (2008-07-03 07:42) [12]> Я бы еще это проверил, не визуально, а прямо перед установкой
> в true в обработчике.
врядли. Но проверю.
> Измениться может много, ведь содержимое процедуры неизвестно,
> возможно пока она работает успевает прийти еще одно сообщение,
> два не может Виндоус не позволит.
Теоретически может, но врядли. LogStr отрабатывает очень быстро (не более 1 мс), а у таймера таймаут 50 мс.
P.S. Не удивлюсь, есть откроется очередной косяк в винде ;)))
... или где-то в программе (все равно не удивлюсь)
← →
Anatoly Podgoretsky © (2008-07-03 07:46) [13]> Loginov Dmitry (03.07.2008 7:42:12) [12]
Это твои утверждения, ничем не подтвержденое, но обычный здравый смысл и безопасность говорит, раз ты отключаешь таймер, то ты должен сделать это первой командой, и только потом все остальное.
← →
Loginov Dmitry © (2008-07-03 09:48) [14]Разобрался. Глюк был в DCOM (получается, что все-таки винда глючит! :). После установки в FormShow
MyTimer.Enabled=True
происходит обращение к DCOM, которое длится некоторое время (в моем случае около 200 мс). Пока происходит данное обращение, винда кладет WM_TIMER в очередь и не проверяет, есть ли уже данное сообщение в очереди. Всех тонкостей я не знаю, но с виду выглядет именно так. Если увеличить время вызываемой по DCOM функции, то увеличится число срабатываний таймера. В лог у меня вывелось следующее (Sleep(10) я убрал):
03.07.2008 09:47:38.980 PlatezTimer.Enabled := True
03.07.2008 09:47:41.889 End of FormShow
03.07.2008 09:47:41.889 [информ.] Before PlatezTimerTimer!
03.07.2008 09:47:41.889 [информ.] After PlatezTimerTimer!
03.07.2008 09:47:41.889 [информ.] Before PlatezTimerTimer!
03.07.2008 09:47:41.889 [информ.] After PlatezTimerTimer!
03.07.2008 09:47:41.889 [информ.] Before PlatezTimerTimer!
03.07.2008 09:47:41.889 [информ.] After PlatezTimerTimer!
03.07.2008 09:47:41.889 [информ.] Before PlatezTimerTimer!
03.07.2008 09:47:41.889 [информ.] After PlatezTimerTimer!
03.07.2008 09:47:41.889 [информ.] Before PlatezTimerTimer!
03.07.2008 09:47:41.889 [информ.] After PlatezTimerTimer!
03.07.2008 09:47:41.889 [информ.] Before PlatezTimerTimer!
03.07.2008 09:47:41.889 [информ.] After PlatezTimerTimer!
03.07.2008 09:47:41.889 [информ.] Before PlatezTimerTimer!
03.07.2008 09:47:41.889 [информ.] After PlatezTimerTimer!
03.07.2008 09:47:41.889 [информ.] Before PlatezTimerTimer!
03.07.2008 09:47:41.889 [информ.] After PlatezTimerTimer!
03.07.2008 09:47:41.889 [информ.] Before PlatezTimerTimer!
03.07.2008 09:47:41.889 [информ.] After PlatezTimerTimer!
03.07.2008 09:47:41.905 [информ.] Before PlatezTimerTimer!
03.07.2008 09:47:41.905 [информ.] After PlatezTimerTimer!
03.07.2008 09:47:41.905 [информ.] Before PlatezTimerTimer!
03.07.2008 09:47:41.905 [информ.] After PlatezTimerTimer!
03.07.2008 09:47:41.905 [информ.] Before PlatezTimerTimer!
03.07.2008 09:47:41.905 [информ.] After PlatezTimerTimer!
03.07.2008 09:47:41.905 [информ.] Before PlatezTimerTimer!
03.07.2008 09:47:41.905 [информ.] After PlatezTimerTimer!
03.07.2008 09:47:41.905 [информ.] Before PlatezTimerTimer!
03.07.2008 09:47:41.905 [информ.] After PlatezTimerTimer!
03.07.2008 09:47:41.905 [информ.] Before PlatezTimerTimer!
03.07.2008 09:47:41.905 [информ.] After PlatezTimerTimer!
03.07.2008 09:47:41.905 [информ.] Before PlatezTimerTimer!
03.07.2008 09:47:41.905 [информ.] After PlatezTimerTimer!
03.07.2008 09:47:41.905 [информ.] Before PlatezTimerTimer!
03.07.2008 09:47:41.905 [информ.] After PlatezTimerTimer!
03.07.2008 09:47:41.905 [информ.] Before PlatezTimerTimer!
03.07.2008 09:47:41.905 [информ.] After PlatezTimerTimer!
03.07.2008 09:47:41.905 [информ.] Before PlatezTimerTimer!
03.07.2008 09:47:41.905 [информ.] After PlatezTimerTimer!
03.07.2008 09:47:41.905 [информ.] Before PlatezTimerTimer!
03.07.2008 09:47:41.905 [информ.] After PlatezTimerTimer!
03.07.2008 09:47:41.905 [информ.] Before PlatezTimerTimer!
03.07.2008 09:47:41.905 [информ.] After PlatezTimerTimer!
03.07.2008 09:47:41.905 [информ.] Before PlatezTimerTimer!
03.07.2008 09:47:41.905 [информ.] After PlatezTimerTimer!
03.07.2008 09:47:41.905 [информ.] Before PlatezTimerTimer!
03.07.2008 09:47:41.905 [информ.] After PlatezTimerTimer!
т.е. все сообщения WM_TIMER обработались скопом.
← →
Игорь Шевченко © (2008-07-03 10:05) [15]
> получается, что все-таки винда глючит!
Руки выпрями и винда перестанет глючить
← →
MsGuns © (2008-07-03 10:10) [16]Офигеть ! Каким боком таймер имеет отношнние к DCOMу ?
Разбирательство ради самого разбирательства есть чистейший мазохизм и просто удивительно, если оно присуще ПРАКТИКУ, призванному прежде всего решать ПРАКТИЧЕСКИЕ задачи.
← →
Loginov Dmitry © (2008-07-03 10:15) [17]
> Каким боком таймер имеет отношнние к DCOMу ?
Таймер связан с очередью сообщений. DCOM - тоже. Вот такое отношение и выливается в bug.
← →
Loginov Dmitry © (2008-07-03 10:19) [18]
> просто удивительно, если оно присуще ПРАКТИКУ, призванному
> прежде всего решать ПРАКТИЧЕСКИЕ задачи.
Хотелось просто для себя самого убедиться, что не я глючу.
← →
Юрий Зотов © (2008-07-03 10:24) [19]> Loginov Dmitry © (03.07.08 09:48) [14]
> Глюк был в DCOM (получается, что все-таки винда глючит!
А теперь разберемся, действительно ли это так.
> После установки в FormShow
> MyTimer.Enabled=True
> происходит обращение к DCOM, которое длится некоторое время
> (в моем случае около 200 мс).
Где тут глюк DCOM - непонятно. Пока что его не видно.
> Пока происходит данное обращение, винда кладет WM_TIMER в очередь
Где тут глюк DCOM - снова непонятно. Пока что его все еще не видно.
> и не проверяет, есть ли уже данное сообщение в очереди.
Почему Винда должна это проверять - тоже непонятно. Таймер тикнул - сообщение поступило. Ну а уж DCOM тут совсем никаким боком.
Возвращаемся к исходному утверждению:
> Глюк был в DCOM (получается, что все-таки винда глючит!
Дима, извини за самоцитату, но придется. Вот что я когда-то написал в "уроках для начинающих" (притом, большими буквами и с восклицательными знаками - обрати внимание):
"И ЗАПОМНИТЕ НА ВСЮ СВОЮ ОСТАВШУЮСЯ ПРОГРАММИСТСКУЮ ЖИЗНЬ - ВО ВСЕХ ГЛЮКАХ ВИНИТЕ СНАЧАЛА СЕБЯ, А УЖ ПОТОМ МАШИНУ, СИСТЕМУ, DELPHI И СОСЕДА ДЯДЮ ВАСЮ. И ошибку ВСЕГДА ищите прежде всего в СВОЕЙ программе!!! И чем чаще Вы будете нарушать это железное правило - тем больше времени и нервов Вы будете тратить на поиск и устранение ошибок. С момента появления первой ЭВМ этот закон проверен уже миллионы раз, всеми поколениями программистов, на всех машинах системах и языках".
← →
MsGuns © (2008-07-03 10:27) [20]>Loginov Dmitry © (03.07.08 10:15) [17]
>Таймер связан с очередью сообщений.
Еще раз спрашиваю КАКИМ БОКОМ ?
Если вы по событию таймера (который вообще-то сам по себе) чего-то там с чем-то пытаетесь синхронизировать или опрашивать, то причем здесь винда вообще и DCOM в частности.
Ей Богу, возникает ощущение, что вы пытаетесь на лыжах ездить по асфальту, а когда падаете, обвиняете дорожников и производителя лыж, но никак не себя, любимого ;)
← →
Anatoly Podgoretsky © (2008-07-03 10:28) [21]около 200 мс и время таймера 50 мс и блокирование таймера не сразу а после выполнения чего то в какой то неизвестной процедуре.
Не ищи ошибки в Виндоус, она не там, Юрий уже сказал где искать, но там ты начинаешь искать в последнею очередь, поэтому у тебя очень много подобных веток.
← →
Loginov Dmitry © (2008-07-03 10:36) [22]
> Почему Винда должна это проверять - тоже непонятно. Таймер
> тикнул - сообщение поступило.
См. [8]
> Не ищи ошибки в Виндоус, она не там, Юрий уже сказал где
> искать, но там ты начинаешь искать в последнею очередь,
> поэтому у тебя очень много подобных веток.
Ладно. Пусть каждый останется при своем мнении. Я вывод для себя сделал. Вы для себя. Бестолку стараться кого-то в чем-то убедить.
Проблему исправил путем переноса обращений к DCOM на строку выше, чем включение таймера. Пре проблемы решились. Спасибо за содействие!
← →
MsGuns © (2008-07-03 10:39) [23]>Проблему исправил путем переноса обращений к DCOM на строку выше, чем включение таймера. >Пре проблемы решились.
Ага. На смену им придут проблемищи
← →
McSimm © (2008-07-03 10:43) [24]А если заменить вызов DCOM на любую другую работу равной длительности, то проблема не воспроизводится ?
> Юрий Зотов © (03.07.08 10:24) [19]
Так не должны вроде сообщения таймера накапливаться, пока первое не обслужено? А тут похоже накапливаются.
← →
Loginov Dmitry © (2008-07-03 10:48) [25]
> А если заменить вызов DCOM на любую другую работу равной
> длительности, то проблема не воспроизводится ?
проблема возникает только при вызове DCOM-функции.
установка Sleep() тойже длительности к ошибке не приводит. Пытался по всякому, даже комбинировал с ProcessMessages.
← →
Loginov Dmitry © (2008-07-03 10:51) [26]
> проблема возникает только при вызове DCOM-функции.
причем не важно какой функции. Работаю через TDCOMConnection. С проблемой столкнулся при открытии набора данных с помощью TClientDataSet.Open (200 мс как раз на это уходило). Но точно также ошибка воспроизводится при вызове любой "медленной" функции через TDCOMConnection.AppServer
← →
McSimm © (2008-07-03 11:02) [27]
> С этим столкнулся на работе. Дома ситуация не воспроизводится.
А как конфигурации отличаются, интересно ?
← →
Юрий Зотов © (2008-07-03 11:03) [28]> McSimm © (03.07.08 10:43) [24]
> Так не должны вроде сообщения таймера накапливаться,
> пока первое не обслужено? А тут похоже накапливаются.
А разве оно не обслужено?
Из очереди оно выбрано? Да.
Из очереди оно удалено? Да (и этого уже достаточно).
В WndProc поступило? Да.
По завершении обработки Msg.Result равно нулю? Да.
← →
Loginov Dmitry © (2008-07-03 11:06) [29]
> А как конфигурации отличаются, интересно ?
Я только вчера TClientDataSet.Open на работе добавил. Дома просто была "устаревшая" версия исходников.
> А разве оно не обслужено?
Нет. Это видно из лога в [14]
← →
Riply © (2008-07-03 11:18) [30]> [16] MsGuns © (03.07.08 10:10)
> Разбирательство ради самого разбирательства есть чистейший мазохизм и просто удивительно,
> если оно присуще ПРАКТИКУ, призванному прежде всего решать ПРАКТИЧЕСКИЕ задачи.
"Разбирательство" помогает понять суть происходящего и не допускать
обидных и досадных ляпов в будущем.
Грош цена "ПРАКТИКУ", который, столкнувшись с непонятной ему вещью,
путем шаманства (потому что не понял) убирает странный эфект и идет дальше.
IMHO разумеется.
← →
McSimm © (2008-07-03 11:27) [31]
> Loginov Dmitry © (03.07.08 11:06)
Можно было бы еще предположить, что логирование блокируется. Хотя обычно логирование добавляют, когда проблема уже есть...
Но в принципе флоу может выглядеть так:
Таймер активирован.
Поступает сообщение WM_TIMER
Обрабатывается сообщение (обработано), вызывается логирование и застряет
Поступает сообщение
Обрабатывается сообщение (обработано), вызывается логирование и застряет
...
Заканчивается работа с DCOM
Убирается (каким-то образом) блокировка
Все ожидавшие возможности записать - записывают.
При этом таймер деактивируется
← →
clickmaker © (2008-07-03 11:27) [32]> Если вы по событию таймера (который вообще-то сам по себе)
вообще-то, не сам по себе
потому как
An application can process WM_TIMER messages by including a WM_TIMER case statement in the window procedure or by specifying a TimerProc callback function when creating the timer. When you specify a TimerProc callback function, the default window procedure calls the callback function when it processes WM_TIMER. Therefore, you need to dispatch messages in the calling thread, even when you use TimerProc instead of processing WM_TIMER.
(c) MSDN
← →
Loginov Dmitry © (2008-07-03 11:34) [33]
> Можно было бы еще предположить, что логирование блокируется.
> Хотя обычно логирование добавляют, когда проблема уже есть.
> ..
Добавление логгирования было связано с возникновением данной проблемы.
← →
Юрий Зотов © (2008-07-03 11:43) [34]> Loginov Dmitry © (03.07.08 11:06) [29]
Когда срабатывает обработчик события OnTimer, вызвавшего его сообщения WM_TIMER в очереди УЖЕ нет. То есть, никакого накопления быть не может.
function TApplication.ProcessMessage(var Msg: TMsg): Boolean;
...
begin
...
if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then
begin
... // Здесь сообщение УЖЕ удалено из очереди (из-за PM_REMOVE)
TranslateMessage(Msg);
DispatchMessage(Msg); // И только теперь будет вызван обработчик
...
end;
end;
Напиши обработчик по-человески и забудь о "глюках":
procedure TMyForm.MyTimerTimer(Sender: TObject);
begin
MyTimer.Enabled := False;
try
... // Здесь делай что угодно и сколько угодно, только не трогай таймер
finally
MyTimer.Enabled := True;
end;
end;
← →
Anatoly Podgoretsky © (2008-07-03 11:53) [35]> Loginov Dmitry (03.07.2008 10:36:22) [22]
> Бестолку стараться кого-то в чем-то убедить.
Если бы я пытался, только в чем убеждать в статистике, так это же не вопрос.
← →
Anatoly Podgoretsky © (2008-07-03 11:55) [36]> McSimm (03.07.2008 10:43:24) [24]
Ты неправильно интерпритируешь ситуацию
begin
Log; Вот тут приходит новое сообщение и так далее. И ничего не накапливается.
Enabled:= False: Рано или поздно вот это отработает и шторм прекратится.
← →
Loginov Dmitry © (2008-07-03 11:58) [37]
> Когда срабатывает обработчик события OnTimer, вызвавшего
> его сообщения WM_TIMER в очереди УЖЕ нет. То есть, никакого
> накопления быть не может.
Именно этого сообщения в очереди нет. Но есть другие (вопреки MSDN)
> Напиши обработчик по-человески и забудь о "глюках":
Вот именно так "по-человечески" оно и было. И глючило.
Юрий, Вы просто не верите, думаете что я пургу гоню. Но это не так. Стал бы я в этом случае создавать ветку?
← →
Loginov Dmitry © (2008-07-03 11:59) [38]
> Ты неправильно интерпритируешь ситуацию
>
>
> begin
> Log; Вот тут приходит новое сообщение и так далее. И
> ничего не накапливается.
> Enabled:= False: Рано или поздно вот это отработает и
> шторм прекратится.
Анатолий, я эти строки давно поменял местами. Это ничего не дало.
← →
Юрий Зотов © (2008-07-03 12:01) [39]> Loginov Dmitry © (03.07.08 11:58) [37]
> Именно этого сообщения в очереди нет. Но есть другие (вопреки MSDN)
Почему "вопреки"?
← →
Anatoly Podgoretsky © (2008-07-03 12:07) [40]> Loginov Dmitry (03.07.2008 11:58:37) [37]
Если этого сообщения нет, то как Виндоус может объеденить?
Это же не Копперфильдовские фокусы.
Страницы: 1 2 вся ветка
Форум: "Начинающим";
Текущий архив: 2008.08.03;
Скачать: [xml.tar.bz2];
Память: 0.58 MB
Время: 0.112 c