Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.14.133.148] |
|
Сообщ.
#1
,
|
|
|
Отправлено 2 сообщения (и оба True - проверено). Но! Выводится сообщение True (т.е. PeekMessage поймал сообщение). А дальше поток зависает на GetMessage. Но ведь в очереди есть ещё одно сообщение!!! Даже если заменить PM_REMOVE на PM_NOREMOVE, всё равно ничего не меняется (хоть PeekMessage и не должен вынимать даже первое сообщение).
В чём проблема-то? p.s. Наличие TranslateMessage(Msg) и DispatchMessage(Msg) не влияет на ситуацию (собственно, и не должно). type TMyThread = class(TThread) Started: Boolean; procedure Execute; override; end; procedure TMyThread.Execute; var Msg: TMsg; begin PeekMessage(Msg, 0, 0, 0, PM_REMOVE); // Создаём очередь сообщений Started := True; Sleep(1000); MessageBox(0, PChar(BoolToStr(PeekMessage(Msg, 0, 0, 0, PM_REMOVE), True)), '', MB_OK); GetMessage(Msg, 0, 0, 0); MessageBox(0, PChar(IntToStr(Msg.message)), '', MB_OK) end; procedure TForm1.Button1Click(Sender: TObject); begin with TMyThread.Create(True) do begin Started := False; Resume; while not Started do Sleep(1); PostThreadMessage(ThreadId, WM_USER, 0, 0); PostThreadMessage(ThreadId, WM_USER+1, 0, 0) end end; |
Сообщ.
#2
,
|
|
|
Jin X
Так удаляются все сообщения. https://msdn.microsoft.com/ru-ru/library/wi...6(v=vs.85).aspx Цитата PM_REMOVE 0x0001 Messages are removed from the queue after processing by PeekMessage https://msdn.microsoft.com/ru-ru/library/wi...6(v=vs.85).aspx Цитата Unlike GetMessage, the PeekMessage function does not wait for a message to be posted before returning. Специфика. Она всегда ждёт. Тайм ауты делаем и всё работает. type TMyThread = class(TThread) Started: Boolean; procedure Execute; override; end; procedure TMyThread.Execute; var Msg: TMsg; begin PeekMessage(Msg, 0, 0, 0, PM_REMOVE); // Ñîçäà¸ì î÷åðåäü ñîîáùåíèé Started := True; Sleep(100); MessageBox(0, PChar(BoolToStr(PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE), True)), '', MB_OK); GetMessage(Msg, 0, 0, 0); MessageBox(0, PChar(IntToStr(Msg.message)), '', MB_OK) end; procedure TForm1.Button1Click(Sender: TObject); begin with TMyThread.Create(True) do begin Started := False; Resume; while not Started do Sleep(1); PostThreadMessage(ThreadId, WM_USER, 0, 0); sleep(10000); PostThreadMessage(ThreadId, WM_USER+1, 0, 0) end end; |
Сообщ.
#3
,
|
|
|
Вообще, я полагал, что он удаляет только те "messageS", которые идут до того, который ему нужен, а оказывается, что все...
Ну это ладно. Даже если я ставлю PM_NOREMOVE, результат тот же. Почему? Мне нужно сделать что-то типа if PeekMessage then GetMessage (я хотел сделать это через один PeekMessage(PM_REMOVE), но раз он удаляет ВСЕ сообщения, но хотя бы через if-то должен работать!) |
Сообщ.
#4
,
|
|
|
Jin X
Немного пореверлся. У меня для вас 2 новости одна хорошая другая плохая. Хорошая что в XE10.2 код работает как надо. А вот плохая в D7 если окно NULL GetMessage кидает исключение, а D7 его перехватывает и направляет на обработчик сообщений основной формы. И я не знаю как это исправить. |
Сообщ.
#5
,
|
|
|
Проблема в MessageBox... по ходу, он сжирает это сообщение как-то.
Убрал MessageBox'ы - всё заработало. Причём, PM_REMOVE удаляет только то сообщение, которое стоит в фильтре. Скажем, если я посылаю сначала WM_USER+1, затем WM_USER и фильтрую WM_USER, то он именно его и удалит, а WM_USER+1 оставит! |
Сообщ.
#6
,
|
|
|
Цитата Jin X @ Проблема в MessageBox... по ходу, он сжирает это сообщение как-то. Внутри любого модального диалога крутится собственный цикл выборки сообщений, который собс-но и "сжирает" все сообщения |
Сообщ.
#7
,
|
|
|
Цитата leo @ Да, это я уже понял Внутри любого модального диалога крутится собственный цикл выборки сообщений, который собс-но и "сжирает" все сообщения Тут один товарищ говорит, что взаимодействие с экраном нельзя делать из потока (типа не только не VCL). Что скажете? |
Сообщ.
#8
,
|
|
|
Jin X
Можно через WinAPI. Делать. Там для этого специальная функция есть - GdiFlush(). https://msdn.microsoft.com/en-us/windows/ha...onous-rendering GdiFlush() https://msdn.microsoft.com/en-us/library/wi...4(v=vs.85).aspx leo А почему тогда в XE рабтает? |
Сообщ.
#9
,
|
|
|
Цитата Pavia @ Не понимаю вообще, с какой стати исключение возникает? У меня на 7 ничего такого не происходит...А вот плохая в D7 если окно NULL GetMessage кидает исключение, а D7 его перехватывает и направляет на обработчик сообщений основной формы. Это прям с моим кодом происходит? Добавлено Вот, кстати... type TMyThread = class(TThread) Started: Boolean; procedure Execute; override; end; procedure TMyThread.Execute; var Msg: TMsg; Res: Boolean; Msg1, Msg2: Integer; begin PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE); // Ñîçäà¸ì î÷åðåäü ñîîáùåíèé Started := True; Sleep(1000); Res := PeekMessage(Msg, 0, WM_USER, WM_USER, PM_REMOVE); GetMessage(Msg, 0, WM_USER+1, WM_USER+1); Msg1 := Msg.message; GetMessage(Msg, 0, 0, 0); Msg2 := Msg.message; MessageBox(0, PChar(BoolToStr(Res, True)+' '+IntToStr(Msg1)+' '+IntToStr(Msg2)), '', MB_OK) end; procedure TForm1.Button1Click(Sender: TObject); begin with TMyThread.Create(True) do begin Started := False; Resume; while not Started do Sleep(1); PostThreadMessage(ThreadId, WM_USER+2, 0, 0); PostThreadMessage(ThreadId, WM_USER+1, 0, 0); PostThreadMessage(ThreadId, WM_USER, 0, 0); end end; Выдаёт True, 1025, 1026. Т.е. из очереди удаляется (хотя в PeekMessage, хоть GetMessage) только то сообщение, которое указано в фильтре, причём одно (это видно, если в PeekMessage сбросить фильтр в 0). Добавлено Цитата Pavia @ Я сейчас залез через дебагер в MessageBox, там действительно этот цикл крутится, только через PeekMessage зачем-то, а потом уходит на WaitMessage. А почему тогда в XE рабтает? |
Сообщ.
#10
,
|
|
|
Цитата Jin X @ Это прям с моим кодом происходит? Это прям внутри GetMessage() происходит. Она сверяет хэндел окна с нулём и если оно 0, то кидает исключение. D7 его даже не кажет оно там жёстко запрятано в system.pas. |
Сообщ.
#11
,
|
|
|
Цитата Pavia @ Для чего, "для этого"? Если я вызываю MessageBox, мне GdiFlush не нужен. Если создаю окно - тоже.Можно через WinAPI. Делать. Там для этого специальная функция есть - GdiFlush(). А всякие специфические рисования – это уже другое дело. |
Сообщ.
#12
,
|
|
|
Цитата Pavia @ Странно, у меня такого не происходит... Прикрепи свой код сюда вместе с EXE-шником.Это прям внутри GetMessage() происходит. Она сверяет хэндел окна с нулём и если оно 0, то кидает исключение. D7 его даже не кажет оно там жёстко запрятано в system.pas. Вот это выдаёт исключения? Прикреплённый файлTest.rar (159,89 Кбайт, скачиваний: 68) |
Сообщ.
#13
,
|
|
|
Jin X
Цитата Jin X @ Для чего, "для этого"? Ты спросил про VCL и экран. Цитата Jin X @ Тут один товарищ говорит, что взаимодействие с экраном нельзя делать из потока (типа не только не VCL). Что скажете? Я ответил что с экраном взаимодействовать можно. Но для этого надо применять GdiFlush() VCL не использует GdiFlush. Поэтому VCL следует оборачивать в Synchronize - он вызывает функцию из основного потока. Что касается окошек из другого потока. То тут я не в курсе. Но большинство оконных функций (WinAPI) работает на сообщениях и они считаются потоко безопасными. Цитата Jin X @ Странно, у меня такого не происходит... Прикрепи свой код сюда вместе с EXE-шником. Твой код из первого сообщений поставь бряк на GetMessage(Msg, 0, 0, 0); И по нажимай F7 - до тех пор пока код не выйдет из этой функции. |
Сообщ.
#14
,
|
|
|
Там у меня 2 раза "не" по ошибке
Цитата Pavia @ Ну там с VCL я так понял основная проблема в том, что он не реентерабельный, включая отсутствие работы с критическими секциями (не копался, но вроде как так).VCL не использует GdiFlush. Поэтому VCL следует оборачивать в Synchronize - он вызывает функцию из основного потока. Цитата Pavia @ А если не дебажить, исключения не будет? И по нажимай F7 - до тех пор пока код не выйдет из этой функции. |
Сообщ.
#15
,
|
|
|
Скрытый текст Цитата Jin X @ проблема в том, что он не реентерабельный, включая отсутствие работы с критическими секциями (не копался, но вроде как так). Критические секции ведут к дедлокам. Спинлоки в этом плане как-то по понятие. Но тоже народ тоже не задумывается о дедлоках. Поэтому они тоже приводят к дедлокам. Тогда как же сделать потокобезопасное приложение? теория на этот счёт говорит просто если у каждого потока будут свои данные то код будет потокобезопасен. Как сделать что-бы у каждого были свои данные? Сообщения. сообщения это данные которые копируются каждые своему потоку. Хотя вот такие вот выкрутасы D7 вымораживают. Лучше наверно реализовать свою очередь сообщений. Классы VCL - потока не безопасен по тому что это общий ресурс(общие классы), а методы не защищены. В этом плане QT лучше у них есть уровень абстракции который инстацирует объекты для потоков. В QT для вех системных методов и они туда могут заложить защиту. Правда говоря откровенно их это их не спасает. Просто не везде защита есть, а если её сделать везде то то система будет тормозить. Но лично я решаю проблемы архитектурно шаблон генерал-подчинённый. Там дедлоки исключены. У каждого потока свои данные, а передачу данных от одного потока к другому осуществляет генерал. За графику и окошки отвечает один поток. - пользователь у нас один ему хватит и одного потока. А вот обработку данных делают разные потоки. Есть и второй способ уйти на низкий уровень. Пул потоков, и собственные примитивы синхронизации. |
Сообщ.
#16
,
|
|
|
Цитата Pavia @ Если делать всё грамотно, никаких дедлоков не будет. Менеджер памяти использует критические секции и ничего не лочится.Критические секции ведут к дедлокам. Спинлоки в этом плане как-то по понятие. Но тоже народ тоже не задумывается о дедлоках. Поэтому они тоже приводят к дедлокам. К тому же, дедлоки происходят тогда, когда используется минимум 2 критические секции (в смысле, объекты - мьютексы). |
Сообщ.
#17
,
|
|
|
Цитата Pavia @ Не понял как это работает.Но лично я решаю проблемы архитектурно шаблон генерал-подчинённый. Там дедлоки исключены. У каждого потока свои данные, а передачу данных от одного потока к другому осуществляет генерал. За графику и окошки отвечает один поток. - пользователь у нас один ему хватит и одного потока. А вот обработку данных делают разные потоки. Вот у потока с GDI есть всякие списки, свойства и пр. Нужно 2-м потокам изменить одновременно эти данные. Что для этого делается? |
Сообщ.
#18
,
|
|
|
Цитата Jin X @ Вот у потока с GDI есть всякие списки, свойства и пр. Нужно 2-м потокам изменить одновременно эти данные. Что для этого делается? Что есть "поток с GDI"? В целом поверьте человеку, наевшемуся с потоками. Во избежание слома мозга из-за таинственных глюков по причине неучстенного совместного доступа к памяти или дедлоков взаимодействие между потоками надо максимально развязывать (см. PostThreadMessage). Данные предпочтительнее копировать, нежели блокировать. Если же блокировок не избежать, они д.б. как можно более короткими. |
Сообщ.
#19
,
|
|
|
Цитата Fr0sT @ Я хочу понять принцип "генерал-подчинённый".Что есть "поток с GDI"? Есть 2 потока, им обоим нужно получить доступ к каким-либо данным. Цитата Fr0sT @ Ты про Delphi конкретно?В целом поверьте человеку, наевшемуся с потоками. Если да, то вопросов нет - тут всё понятно. |
Сообщ.
#20
,
|
|
|
Цитата Jin X @ Я хочу понять принцип "генерал-подчинённый". Есть 2 потока, им обоим нужно получить доступ к каким-либо данным. "принцип "генерал-подчинённый"" это очень расплывчато. Предполагаю, в данном случае это означает, что один поток является "владельцем" данных (может манипулировать ими в любой момент), а остальным позволяется получать к ним доступ только по сигналу. Подходы есть разные. Есть slim reader-writer (как в виде winapi объекта, так и эмуляцией через две крит. секции), когда кол-во одновременно читающих потоков не ограничено. Есть принцип Go - никаких разделяемых данных, строго копирование. Есть классические крит. секции. Цитата Jin X @ Ты про Delphi конкретно? Да нет, это любого языка касается (ну кроме Go, там по-другому и не сделаешь) |
Сообщ.
#21
,
|
|
|
Цитата Fr0sT @ Ну это высокоуровневые проблемы. С WinAPI же вроде как таких проблем нет...Да нет, это любого языка касается Цитата Fr0sT @ Я думаю, что Pavia что-то другое имел в виду, т.к. ридером может быть и основной поток.Есть slim reader-writer (как в виде winapi объекта, так и эмуляцией через две крит. секции) Цитата Fr0sT @ Что значит "копирование" в данном случае? Чтение? А как же запись? Есть принцип Go - никаких разделяемых данных, строго копирование. |
Сообщ.
#22
,
|
|
|
Цитата Jin X @ С WinAPI же вроде как таких проблем нет... Такие проблемы есть везде, где есть потоки и разделяемые области памяти. Хоть на ассемблере Цитата Jin X @ Что значит "копирование" в данном случае? Чтение? А как же запись? Копирование - это копирование. Записи никакой не предусмотрено. Надо передать потоку данные - отправляй копию. Жёстко, но полностью исключает проблемы совместного доступа |
Сообщ.
#23
,
|
|
|
Цитата Fr0sT @ А что с записью? Тоже через посредника?Копирование - это копирование. Записи никакой не предусмотрено. Надо передать потоку данные - отправляй копию. Жёстко, но полностью исключает проблемы совместного доступа Цитата Fr0sT @ Давай пример при работы с GDI через WinAPI из 2-х потоков одновременно. Но чтобы косяк был не в коде (типа обработчика сообщений, который пишет куда-то). Такие проблемы есть везде, где есть потоки и разделяемые области памяти. |
Сообщ.
#24
,
|
|
|
Цитата Jin X @ А что с записью? Тоже через посредника? Записью чего? Куда? Откуда? Данные существуют только в одном экземпляре, у владельца. Передача через копирование. Что непонятного? Цитата Jin X @ Давай пример при работы с GDI через WinAPI из 2-х потоков одновременно. Но чтобы косяк был не в коде (типа обработчика сообщений, который пишет куда-то). Разговор зашел не в ту степь. Есть вопрос - спрашивай конкретно, давать какие-то примеры для доказательства непонятно чего мне совсем не вперлось |
Сообщ.
#25
,
|
|
|
Цитата Fr0sT @ Речь начиналась о критических секциях, насколько я помню.Записью чего? Куда? Откуда? Данные существуют только в одном экземпляре, у владельца. Передача через копирование. Что непонятного? Кто-то читает данные, а кто-то их пишет. Если чтение производится через посредника путём передачи копии, то как в этом механизме происходит запись этих данных? Цитата Fr0sT @ Причём тут доказательство? Я хочу понять что ты имеешь в виду: в каких случаях (например) возможны проблемы при обращении к WinAPI (GDI) одновременно двух потоков? Разговор зашел не в ту степь. Есть вопрос - спрашивай конкретно, давать какие-то примеры для доказательства непонятно чего мне совсем не вперлось |
Сообщ.
#26
,
|
|
|
Цитата Jin X @ Если чтение производится через посредника путём передачи копии, то как в этом механизме происходит запись этих данных? Так же как и чтение. Потоку-владельцу присылается копия данных, он уже решает, что с ними делать Цитата Jin X @ Я хочу понять что ты имеешь в виду: в каких случаях (например) возможны проблемы при обращении к WinAPI (GDI) одновременно двух потоков? Да легко. Попробуй рисовать на канвасе из двух потоков одновременно. Многие объекты в winapi thread-safe, но не все. |
Сообщ.
#27
,
|
|
|
Цитата Jin X @ в каких случаях (например) возможны проблемы при обращении к WinAPI (GDI) одновременно двух потоков? Чего ты привязался к этому "WinAPI (GDI)"? Конкретизируй вопрос. Первоначально речь шла только о показе MessageBox из доп.потока. Тут "проблемы" могут быть только в логике организации взаимодействия с юзером. Например, блокируешь ты основную форму на время показа MessageBox или нет. При hWnd=0 (как у тебя в примере #1) форма не блокируется и оба окна ведут себя независимо, поэтому весь расчет на то, что разумный юзер должен сначала закрыть МБ прежде чем тыкать кнопки\меню на форме. Но иногда возможны нештатные ситуации, когда в результате "не пойми чего" форма вылезает на передний план, закрывая МБ, и юзер (не замечая второго окна приложения на панели задач) начинает тыкать по форме, а МБ продолжает "благополучно висеть" под формой вместе со своим потоком. Если же блокировать форму на время показа МБ (передавая ее hWnd в МБ), то ситуация становится более понятной\предсказуемой, но только в том случае, если показывается только один МБ. Если же у тебя несколько потоков могут одновременно показывать МБ (или не дай бог основной поток выдаст ShowMessage), то ситуация может быть еще более запутанной\непредсказуемой. Видимо "один товарищ" (упомянутый в посте #7) это и имел ввиду, а не то, что возможны какие-то критические ситуации с дедлоками, AV и т.п. Добавлено Цитата Fr0sT @ Да легко. Попробуй рисовать на канвасе из двух потоков одновременно. На разных канвасах (оконных DC) - "легко". Винда без проблем отрисовывает десятки перекрывающихся окон, даже если они пытаются рисовать себя полностью, а не только видимую часть - все излишние "художества" благополучно вырезаются и на экран выводится общая картинка в соответствии с Z-порядком отображения окон. |
Сообщ.
#28
,
|
|
|
Цитата leo @ На этот случай есть MB_TASKMODAL, который я комбинирую с MB_SETFOREGROUND.Но иногда возможны нештатные ситуации, когда в результате "не пойми чего" форма вылезает на передний план, закрывая МБ, и юзер (не замечая второго окна приложения на панели задач) начинает тыкать по форме, а МБ продолжает "благополучно висеть" под формой вместе со своим потоком. Цитата leo @ Такой задачи-то и нет . Всё надо с умом делать Если же блокировать форму на время показа МБ (передавая ее hWnd в МБ), то ситуация становится более понятной\предсказуемой, но только в том случае, если показывается только один МБ. Если же у тебя несколько потоков могут одновременно показывать МБ (или не дай бог основной поток выдаст ShowMessage), то ситуация может быть еще более запутанной\непредсказуемой Цитата leo @ "Один товарищ", как выяснилось имел в виду ShowMessage, хотя разговор явно шёл про MessageBox. Ну перепутал он Видимо "один товарищ" (упомянутый в посте #7) Цитата Fr0sT @ Так, а что будет-то? Мне прямо даже интересно Да легко. Попробуй рисовать на канвасе из двух потоков одновременно. Многие объекты в winapi thread-safe, но не все. И где-то вообще есть инфа о не-thread-safe-функциях? |
Сообщ.
#29
,
|
|
|
Цитата leo @ На разных канвасах (оконных DC) - "легко". Винда без проблем отрисовывает десятки перекрывающихся окон, даже если они пытаются рисовать себя полностью, а не только видимую часть - все излишние "художества" благополучно вырезаются и на экран выводится общая картинка в соответствии с Z-порядком отображения окон. Хитрый какой! )) Нет, я имел в виду на одном канвасе. |