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

Вниз

Таймер и поток   Найти похожие ветки 

 
Кто б сомневался ©   (2015-03-09 02:35) [0]

Непонятно..
Есть базовый vcl поток и второй поток. Со второго потока создается и запускается таймер. При срабатывании WM_TIMER код будет выполняться в каком потоке и почему?

constructor TSimpleTimer.Create(AInterval: integer);
begin
 inherited Create;
 fWindowHWND := Classes.AllocateHWnd(WndProc);
 fInterval := AInterval;
end;

procedure TSimpleTimer.WndProc(var AMsg: TMessage);
begin
 with AMsg do
 begin
   if (Msg = WM_TIMER) then
   begin
     Assert(Assigned(fOnTimer), "Set OnTimer event");
     if Assigned(fOnTimer) then
       fOnTimer(Self)
   end
   else
     Result := DefWindowProc(fWindowHWND, Msg, wParam, lParam);
 end;
end;


Classes.pas:
function AllocateHWnd(const AMethod: TWndMethod): HWND;
var
 TempClass: TWndClass;
 ClassRegistered: Boolean;
begin
 UtilWindowClass.hInstance := HInstance;
 ClassRegistered := GetClassInfo(HInstance, UtilWindowClass.lpszClassName,
   TempClass);
 if not ClassRegistered or (TempClass.lpfnWndProc <> @DefWindowProc) then
 begin
   if ClassRegistered then
     Winapi.Windows.UnregisterClass(UtilWindowClass.lpszClassName, HInstance);
   Winapi.Windows.RegisterClass(UtilWindowClass);
 end;
 Result := CreateWindowEx(WS_EX_TOOLWINDOW, UtilWindowClass.lpszClassName,
   "", WS_POPUP {+ 0}, 0, 0, 0, 0, 0, 0, HInstance, nil);
 if Assigned(AMethod) then
   SetWindowLongPtr(Result, GWL_WNDPROC, IntPtr(MakeObjectInstance(AMethod)));
end;


Спасибо.


 
Кто б сомневался ©   (2015-03-09 02:36) [1]

Вот блин. Не ту кнопку нажал.

constructor TSimpleTimer.Create(AInterval: integer);
begin
 inherited Create;
 fWindowHWND := Classes.AllocateHWnd(WndProc);
 fInterval := AInterval;
end;

procedure TSimpleTimer.WndProc(var AMsg: TMessage);
begin
 with AMsg do
 begin
   if (Msg = WM_TIMER) then
   begin
     Assert(Assigned(fOnTimer), "Set OnTimer event");
     if Assigned(fOnTimer) then
       fOnTimer(Self)
   end
   else
     Result := DefWindowProc(fWindowHWND, Msg, wParam, lParam);
 end;
end;


Classes.pas:
function AllocateHWnd(const AMethod: TWndMethod): HWND;
var
 TempClass: TWndClass;
 ClassRegistered: Boolean;
begin
 UtilWindowClass.hInstance := HInstance;
 ClassRegistered := GetClassInfo(HInstance, UtilWindowClass.lpszClassName,
   TempClass);
 if not ClassRegistered or (TempClass.lpfnWndProc <> @DefWindowProc) then
 begin
   if ClassRegistered then
     Winapi.Windows.UnregisterClass(UtilWindowClass.lpszClassName, HInstance);
   Winapi.Windows.RegisterClass(UtilWindowClass);
 end;
 Result := CreateWindowEx(WS_EX_TOOLWINDOW, UtilWindowClass.lpszClassName,
   "", WS_POPUP {+ 0}, 0, 0, 0, 0, 0, 0, HInstance, nil);
 if Assigned(AMethod) then
   SetWindowLongPtr(Result, GWL_WNDPROC, IntPtr(MakeObjectInstance(AMethod)));
end;


 
Кто б сомневался ©   (2015-03-09 02:40) [2]

В базовом, т.к. там цикл GetMessage?


 
DVM ©   (2015-03-09 10:01) [3]


> Кто б сомневался ©   (09.03.15 02:35) 


> При срабатывании WM_TIMER код будет выполняться в каком
> потоке и почему?

Почему бы не вызвать во всех нужных точках программы GetCurrentThreadId и не сравнить числа?


> В базовом, т.к. там цикл GetMessage?

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


 
Leonid Troyanovsky ©   (2015-03-09 10:22) [4]


> Кто б сомневался ©   (09.03.15 02:40) [2]

> В базовом, т.к. там цикл GetMessage?

Ни в одном.
Бо, установивший таймер поток должен иметь цикл обработки сообщений.
RTFM:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms644906(v=vs.85).aspx

Про окно вторичном потоке см. также
http://rsdn.ru/forum/delphi/883505

--
Regards, LVT.


 
Кто б сомневался ©   (2015-03-09 22:49) [5]


> > В базовом, т.к. там цикл GetMessage?
>
> У тебя окно принадлежит второму потоку, а цикл находится
> в первом. Этот цикл выбирает сообщения из очереди первого
> потока, сообщения посланные окнам второго потока туда не
> попадают.


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


 
DVM ©   (2015-03-10 10:14) [6]


> Буду в цикле выполнять код, до этого засекаться время, после
> выполнения кода делать sleep потока на время константу минус
> время потраченное на код.

Костыльно как то выглядит. Вообще, имхо, любой код, где используется sleep() уже подозрителен.


 
Кто б сомневался ©   (2015-03-10 14:07) [7]


> Вообще, имхо, любой код, где используется sleep() уже подозрителен.


Да ладно, подозрительный.
Как раз для паузы (в моем случае это таймаут) - идеально. И поток отдает свое время другим, и пауза считается и легко отследить отладчиком..


 
Кто б сомневался ©   (2015-03-10 14:16) [8]

Костыльно это когда через WM_TIMER, во втором потоке, специально созданному для WM_TIMER невидимому окну.
И хрен знает что с ним случиться, если второй поток случайно навернется.


> и пауза считается


В данном случае это событие (Event) - отсчет таймаута, которое срабатывает каждую секунду, потом в вышестоящих классах отправляется WM на форму.


 
vuk ©   (2015-03-10 14:21) [9]

Если нужен таймер в потоке, то лучше Waitable Timer использовать.


 
Кто б сомневался ©   (2015-03-10 14:38) [10]


> vuk ©   (10.03.15 14:21) [9]
>
> Если нужен таймер в потоке, то лучше Waitable Timer использовать.
>


Это больше для пула потоков подходит. А если все в одном потоке происходит, то имхо проще sleep.


 
MBo ©   (2015-03-10 15:09) [11]

Лучше о задаче расскажи, а то на проблему XYZ смахивает.


 
vuk ©   (2015-03-10 15:13) [12]

Зачем для пула? Создал таймер, выставил период и вперед - WaitForSingleObject/WaitForMultipleObjects. Дождался, отработал, ждешь следующего сигнала. И никаких костылей в виде окон и оконных функций.


 
DVM ©   (2015-03-10 15:14) [13]


> Кто б сомневался ©   (10.03.15 14:38) [10]


> А если все в одном потоке происходит, то имхо проще sleep.


> Да ладно, подозрительный.

Особенно вся эта костыльность проявляет себя при завершении такого потока или при завершении программы. Sleep нельзя прервать, все сидят и ждут когда же этот Sleep вернет управление. Особенно если интервал в Sleep большой. Еще смешнее когда интервал в силу других ошибок в программе становится значительный - все виснет. Есть же события, Waitable Timers, WaitFor функции наконец. Нужды использовать Sleep нет абсолютно никакой, разве что для отладки.


 
Кто б сомневался ©   (2015-03-10 15:45) [14]


> Есть же события, Waitable Timers, WaitFor функции наконец.
>  


ну и чем он функционально отличается от sleep (если используется в одном потоке, а не в пуле)? Так же поток будет пропускать CPU время (waitfor = sleep),  пока таймер отсигналит.


> Нужды использовать Sleep нет абсолютно никакой, разве что
> для отладки.


Ты путаешь sleep и suspendThread - это разные вещи, которые по разному работают.

Sleep - This function causes a thread to relinquish the remainder of its time slice and become unrunnable for an interval based on the value of dwMilliseconds.

suspendThread - suspending a thread causes the thread to stop executing user-mode (application) code. Как пишет Рихтер там около тысячи тактов, довольно тяжелая вещь.


 
Кто б сомневался ©   (2015-03-10 15:51) [15]

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


 
jack128 ©   (2015-03-10 16:03) [16]


> Со второго потока создается и запускается таймер.

MakeObjectInstance не потокобезопасная функция, поэтому VCL"ный таймер нельзя создавать в доп потоке.


 
DVM ©   (2015-03-10 16:20) [17]


> Кто б сомневался ©   (10.03.15 15:45) [14]


> ну и чем он функционально отличается от sleep (если используется
> в одном потоке, а не в пуле)? Так же поток будет пропускать
> CPU время (waitfor = sleep),  пока таймер отсигналит.

1) Sleep нельзя прервать. Для преодоления этого недостатка был создан еще один костыль - SleepEx.
2) Интервал времени, который реально даст Sleep зависит от положения звезд на небе и с очень большой натяжкой сравним с тем что написано в скобках.
3) Крутить Sleep с небольшим интервалом в цикле - это значит обрекать поток на постоянную работу. Он будет засыпать-просыпаться вместо того, чтобы просто ждать нужного события. В целом это пагубно отражается на быстродействии всей системы.


> Ты путаешь sleep и suspendThread - это разные вещи, которые
> по разному работают.

я ничего не путаю, о suspendThread вообще речи не идет.


 
DVM ©   (2015-03-10 16:26) [18]


> 3) Крутить Sleep с небольшим интервалом в цикле - это значит
> обрекать поток на постоянную работу. Он будет засыпать-просыпаться
> вместо того, чтобы просто ждать нужного события. В целом
> это пагубно отражается на быстродействии всей системы.

Если кто-то может подумать, что это фигня и ловля блох, то следует помнить о мобильных платформах.


 
Кто б сомневался ©   (2015-03-10 16:43) [19]


> 3) Крутить Sleep с небольшим интервалом в цикле - это значит
> обрекать поток на постоянную работу. Он будет засыпать-просыпаться
> вместо того, чтобы просто ждать нужного события. В целом
> это пагубно отражается на быстродействии всей системы.


В случае Waitable таймера и слип - я вижу разницу только в точности. И то я сомневаюсь что там большая разница, если использовать timeBeginPeriod для sleep.
(To increase the accuracy of the sleep interval, call the timeGetDevCaps function to determine the supported minimum timer resolution and the timeBeginPeriod function to set the timer resolution to its minimum. )

А вот с точки зрения работы двух методов - это шило на мыло, только больше писанины. Имхо.

при Waitable таймере -
тот же цикл,
создаем, запускаем таймер, далее
- WaitFor (технически это тот же sleep - поток также будет пропускать CPU время).
- Через секунду таймер сработал, сделали работу,
- опять WaitFor (sleep).

И чем это отличается от цикла и sleep?
Тем что в sleep всего одна строчка.


 
Кто б сомневался ©   (2015-03-10 16:59) [20]


> DVM ©   (10.03.15 16:26) [18]

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


 
Кто б сомневался ©   (2015-03-10 17:05) [21]


> Просто в моем случае нужно каждую секунду отсчитать и возбудить
> событие.


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


 
DVM ©   (2015-03-10 17:09) [22]


> Кто б сомневался ©   (10.03.15 16:43) [19]

Отличия в том, что ты функцию ожидания WaitForMultipleObjects ты всегда можешь прервать немедленно и быстро, если ждать в функции два объекта: событие завершения потока и таймер. У тебя в параметрах Sleep  число 1000 стоит? Не очень много. А если бы там минута стояла?


 
Кто б сомневался ©   (2015-03-10 17:50) [23]


> DVM ©   (10.03.15 17:09) [22]
У тебя в параметрах Sleep  число 1000 стоит?


1000 - минус время потраченное на проверку.

Плюс таким образом еще и считаем сколько прошло секунд. Плюс каждую секунду отправляем WM на форму.
Не, имхо все таки вариант со sleep больше здесь подходит. Хотя надо вечером еще подумать.


 
DVM ©   (2015-03-10 18:07) [24]


> 1000 - минус время потраченное на проверку.

главное отрицательное число не получить:

var
 a: DWORD;
begin
 a := 1001;
 Sleep(1000 - a);  // <= висим
end;


 
Кто б сомневался ©   (2015-03-10 18:12) [25]


> DVM ©   (10.03.15 18:07) [24]


Да это я в курсе.
А ты бы сделал через WaitForMultipleObjects?
Но он чаще чем за секунду будет срабатывать. Соотвественно поток чаще будет входить в ожидание WaitFor. Возможно за секунду много раз.
А waitFor этот тот же sleep по факту.


 
Rouse_ ©   (2015-03-10 18:22) [26]


> Кто б сомневался ©   (10.03.15 16:59) [20]
> Я понял, мы про разное говорим. Просто в моем случае нужно
> каждую секунду отсчитать и возбудить событие.

Так может быть тебе дополнительная нить вообще не нужна в таком случае?


 
Кто б сомневался ©   (2015-03-10 18:30) [27]


> Rouse_ ©   (10.03.15 18:22) [26]
>
>
> > Кто б сомневался ©   (10.03.15 16:59) [20]
> > Я понял, мы про разное говорим. Просто в моем случае нужно
>
> > каждую секунду отсчитать и возбудить событие.
>
> Так может быть тебе дополнительная нить вообще не нужна
> в таком случае?


Не, нить точно нужна. Это параллельная задача.
GUI только ловит сообщения от потоков.


 
Rouse_ ©   (2015-03-10 19:01) [28]

Ага, те. нагрузка все-же в нити, а наружу только нотификации.
Есть два варианта: первый через внешний эвент, который будет ждать нить на WaitFor а само событие перезапускается в основной нити (грубо аналог машины состояний реализующий задачу перекрестка с множеством светофоров на одном таймере)
Второй вариант: использование таймера с калбэком, а необходимые для калбэка данные хранить в TLS

Я реализовывал оба варианта, оба хорошие, но первый немного посложнее при наличии множества нитей.


 
Rouse_ ©   (2015-03-10 19:04) [29]

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


 
Rouse_ ©   (2015-03-10 19:08) [30]

Грубо по второму варианту:

1. в Execute нити запоминаем Self во внешней переменной
threadvar: MyThreadObject;

2. Вызываем SetTimer с указанием не HWID окна а адреса калбэка

3. в калбэке вытаскиваем экземпляр класса из MyThreadObject (он будет уникален для каждой нити) и работаем как есть.


 
Кто б сомневался ©   (2015-03-10 19:09) [31]


> А ты бы сделал через WaitForMultipleObjects?

Итак процессы и файлы уже закрываются, ждем их закрытия, показывая юзеру обратный отсчет (таймаут) на каждый объект.

Прикинул в уме - все таки через sleep код прозрачней для программиста получается и меньше (не нужно массив заводить для WaitForMultipleObjects, проверять его каждый раз, а потом еще и обратно ассоциировать с текущими классами, плюс у WaitFor все равно нужно ставить таймаут в секунду - вдруг объект не закроется, плюс корректировать время, чтобы раз в сек. отправлять сообщение).

Вот кстати DVM"у пример, когда вариант со Sleep более удобный, чем с WaitFor.


 
Rouse_ ©   (2015-03-10 19:11) [32]

ЗЗЫ: но есть нюанс, если придется резко тормозить твое ПО, то лучше все-же делать через внешние события, в противном случае допнити будут продолжать висеть


 
Rouse_ ©   (2015-03-10 19:14) [33]


> Вот кстати DVM"у пример, когда вариант со Sleep более удобный,
>  чем с WaitFor.

Sleep - это просто обертка над WaitFor (ну это так, для самообразования, если что :)


 
Дмитрий С ©   (2015-03-10 19:14) [34]


> использование таймера с калбэком

А разве калбек будет работать без цикла GetMessage в том же потоке?


 
Кто б сомневался ©   (2015-03-10 19:16) [35]


> Rouse_ ©   (10.03.15 19:08) [30]


Даже не знаю. Я просто в сообщении передаю ссылку на потоко-защищенный класс. Да и все.


 
Rouse_ ©   (2015-03-10 19:16) [36]


> Дмитрий С ©   (10.03.15 19:14) [34]
> А разве калбек будет работать без цикла GetMessage в том
> же потоке?

Дим, ты начинаешь меня пугать :)


 
Rouse_ ©   (2015-03-10 19:18) [37]


> Кто б сомневался ©   (10.03.15 19:16) [35]
> Даже не знаю. Я просто в сообщении передаю ссылку на потоко-
> защищенный класс. Да и все.

Ну вот попробуй, получится я знаю.


 
Дмитрий С ©   (2015-03-10 19:29) [38]


> Дим, ты начинаешь меня пугать :)

Либо я что-то в теме пропустил, либо пугаешь меня ты.

Я вообще так думаю: Если калбек должен вызываться по таймеру, то кто-то его должен вызывать явно (не по прерыванию же). Ну а кто, кроме оконного цикла? (GetMessage, PeekMessage, TranslateMessage или DispatchMessage - я не помню кого именно и как надо вызывать). Что не так то?


 
Rouse_ ©   (2015-03-10 19:41) [39]


> Что не так то?

Практически все :)
Для калбэка ЦВС не нужен, он вообще тут избыточен ибо играет второстепенную роль.
Я тебе секрет открою - вся асинхронность на ЦВС строится через калбэки, даже сокеты с их асинхронной моделью :)
Читай внимательней MSDN - оно наше все :)


 
Rouse_ ©   (2015-03-10 19:45) [40]

Даже наверное перефразирую - вся эта чехарда с ЦВС (который привязан к конкретной нити, где создано окно) - это всего лишь результат калбэков.
Все эти окна - сообщения, ЦВС - это все наведенное. На самом деле все работает совсем по другому :)



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

Форум: "Прочее";
Текущий архив: 2015.11.01;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.57 MB
Время: 0.003 c
15-1426714204
Юрий
2015-03-19 00:30
2015.11.01
С днем рождения ! 19 марта 2015 четверг


15-1426887004
Юрий
2015-03-21 00:30
2015.11.01
С днем рождения ! 21 марта 2015 суббота


15-1426541405
Юрий
2015-03-17 00:30
2015.11.01
С днем рождения ! 17 марта 2015 вторник


2-1401462501
Андрюша
2014-05-30 19:08
2015.11.01
Как внести в базу Firebird в одно поле дату и время?


15-1426154123
K-1000
2015-03-12 12:55
2015.11.01
Чем вы занимались до прихода компьютера/интернета?





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