Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Начинающим";
Текущий архив: 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
15-1213968206
Small Donkey
2008-06-20 17:23
2008.08.03
Размещение ваших программ


2-1215196825
roof
2008-07-04 22:40
2008.08.03
Randomize & массив


2-1214990952
lewka-sedceed
2008-07-02 13:29
2008.08.03
Работа с ListBox


15-1213304967
Loginov Dmitry
2008-06-13 01:09
2008.08.03
SafeIniFiles


15-1213735378
No_Dead
2008-06-18 00:42
2008.08.03
Разве это законно?





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