На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! ПРАВИЛА РАЗДЕЛА · FAQ раздела Delphi · Книги по Delphi
Пожалуйста, выделяйте текст программы тегом [сode=pas] ... [/сode]. Для этого используйте кнопку [code=pas] в форме ответа или комбобокс, если нужно вставить код на языке, отличном от Дельфи/Паскаля.
Следующие вопросы задаются очень часто, подробно разобраны в FAQ и, поэтому, будут безжалостно удаляться:
1. Преобразовать переменную типа String в тип PChar (PAnsiChar)
2. Как "свернуть" программу в трей.
3. Как "скрыться" от Ctrl + Alt + Del (заблокировать их и т.п.)
4. Как прочитать список файлов, поддиректорий в директории?
5. Как запустить программу/файл?
... (продолжение следует) ...

Вопросы, подробно описанные во встроенной справочной системе Delphi, не несут полезной тематической нагрузки, поэтому будут удаляться.
Запрещается создавать темы с просьбой выполнить какую-то работу за автора темы. Форум является средством общения и общего поиска решения. Вашу работу за Вас никто выполнять не будет.


Внимание
Попытки открытия обсуждений реализации вредоносного ПО, включая различные интерпретации спам-ботов, наказывается предупреждением на 30 дней.
Повторная попытка - 60 дней. Последующие попытки бан.
Мат в разделе - бан на три месяца...
Модераторы: jack128, D[u]fa, Shaggy, Rouse_
  
> Затык с сообщениями , отправляется 2, приходит 1
    Отправлено 2 сообщения (и оба True - проверено). Но! Выводится сообщение True (т.е. PeekMessage поймал сообщение). А дальше поток зависает на GetMessage. Но ведь в очереди есть ещё одно сообщение!!! Даже если заменить PM_REMOVE на PM_NOREMOVE, всё равно ничего не меняется (хоть PeekMessage и не должен вынимать даже первое сообщение).
    В чём проблема-то? :wall:
    p.s. Наличие TranslateMessage(Msg) и DispatchMessage(Msg) не влияет на ситуацию (собственно, и не должно).
    ExpandedWrap disabled
      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;
      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.

      Специфика. Она всегда ждёт.

      Тайм ауты делаем и всё работает.
      ExpandedWrap disabled
        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;
        Вообще, я полагал, что он удаляет только те "messageS", которые идут до того, который ему нужен, а оказывается, что все...
        Ну это ладно. Даже если я ставлю PM_NOREMOVE, результат тот же. Почему?
        Мне нужно сделать что-то типа if PeekMessage then GetMessage (я хотел сделать это через один PeekMessage(PM_REMOVE), но раз он удаляет ВСЕ сообщения, но хотя бы через if-то должен работать!)
          Jin X
          Немного пореверлся. У меня для вас 2 новости одна хорошая другая плохая.
          Хорошая что в XE10.2 код работает как надо.
          А вот плохая в D7 если окно NULL GetMessage кидает исключение, а D7 его перехватывает и направляет на обработчик сообщений основной формы.
          И я не знаю как это исправить.
            Проблема в MessageBox... по ходу, он сжирает это сообщение как-то.
            Убрал MessageBox'ы - всё заработало. Причём, PM_REMOVE удаляет только то сообщение, которое стоит в фильтре.
            Скажем, если я посылаю сначала WM_USER+1, затем WM_USER и фильтрую WM_USER, то он именно его и удалит, а WM_USER+1 оставит!
              Цитата Jin X @
              Проблема в MessageBox... по ходу, он сжирает это сообщение как-то.

              :yes:
              Внутри любого модального диалога крутится собственный цикл выборки сообщений, который собс-но и "сжирает" все сообщения
                Цитата leo @
                Внутри любого модального диалога крутится собственный цикл выборки сообщений, который собс-но и "сжирает" все сообщения
                Да, это я уже понял :)
                Тут один товарищ говорит, что взаимодействие с экраном нельзя делать из потока (типа не только не VCL). Что скажете?
                  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 рабтает?
                    Цитата Pavia @
                    А вот плохая в D7 если окно NULL GetMessage кидает исключение, а D7 его перехватывает и направляет на обработчик сообщений основной формы.
                    Не понимаю вообще, с какой стати исключение возникает? У меня на 7 ничего такого не происходит...
                    Это прям с моим кодом происходит?

                    Добавлено
                    Вот, кстати...
                    ExpandedWrap disabled
                      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 @
                    А почему тогда в XE рабтает?
                    Я сейчас залез через дебагер в MessageBox, там действительно этот цикл крутится, только через PeekMessage зачем-то, а потом уходит на WaitMessage.
                      Цитата Jin X @
                      Это прям с моим кодом происходит?

                      Это прям внутри GetMessage() происходит. Она сверяет хэндел окна с нулём и если оно 0, то кидает исключение. D7 его даже не кажет оно там жёстко запрятано в system.pas.
                        Цитата Pavia @
                        Можно через WinAPI. Делать. Там для этого специальная функция есть - GdiFlush().
                        Для чего, "для этого"? Если я вызываю MessageBox, мне GdiFlush не нужен. Если создаю окно - тоже.
                        А всякие специфические рисования – это уже другое дело.
                          Цитата Pavia @
                          Это прям внутри GetMessage() происходит. Она сверяет хэндел окна с нулём и если оно 0, то кидает исключение. D7 его даже не кажет оно там жёстко запрятано в system.pas.
                          Странно, у меня такого не происходит... Прикрепи свой код сюда вместе с EXE-шником.

                          Вот это выдаёт исключения?
                          Прикреплённый файлПрикреплённый файлTest.rar (159,89 Кбайт, скачиваний: 68)
                            Jin X
                            Цитата Jin X @
                            Для чего, "для этого"?

                            Ты спросил про VCL и экран.
                            Цитата Jin X @
                            Тут один товарищ говорит, что взаимодействие с экраном нельзя делать из потока (типа не только не VCL). Что скажете?

                            Я ответил что с экраном взаимодействовать можно. Но для этого надо применять GdiFlush()
                            VCL не использует GdiFlush. Поэтому VCL следует оборачивать в Synchronize - он вызывает функцию из основного потока.

                            Что касается окошек из другого потока. То тут я не в курсе. Но большинство оконных функций (WinAPI) работает на сообщениях и они считаются потоко безопасными.


                            Цитата Jin X @
                            Странно, у меня такого не происходит... Прикрепи свой код сюда вместе с EXE-шником.

                            Твой код из первого сообщений поставь бряк на GetMessage(Msg, 0, 0, 0);
                            И по нажимай F7 - до тех пор пока код не выйдет из этой функции.
                            Сообщение отредактировано: Pavia -
                              Там у меня 2 раза "не" по ошибке :)
                              Цитата Pavia @
                              VCL не использует GdiFlush. Поэтому VCL следует оборачивать в Synchronize - он вызывает функцию из основного потока.
                              Ну там с VCL я так понял основная проблема в том, что он не реентерабельный, включая отсутствие работы с критическими секциями (не копался, но вроде как так).

                              Цитата Pavia @
                              И по нажимай F7 - до тех пор пока код не выйдет из этой функции.
                              А если не дебажить, исключения не будет?
                                Скрытый текст
                                Цитата Jin X @
                                проблема в том, что он не реентерабельный, включая отсутствие работы с критическими секциями (не копался, но вроде как так).

                                Критические секции ведут к дедлокам. Спинлоки в этом плане как-то по понятие. Но тоже народ тоже не задумывается о дедлоках. Поэтому они тоже приводят к дедлокам.

                                Тогда как же сделать потокобезопасное приложение? теория на этот счёт говорит просто если у каждого потока будут свои данные то код будет потокобезопасен.

                                Как сделать что-бы у каждого были свои данные? Сообщения. сообщения это данные которые копируются каждые своему потоку.

                                Хотя вот такие вот выкрутасы D7 вымораживают. Лучше наверно реализовать свою очередь сообщений.

                                Классы VCL - потока не безопасен по тому что это общий ресурс(общие классы), а методы не защищены.

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

                                Но лично я решаю проблемы архитектурно шаблон генерал-подчинённый. Там дедлоки исключены.

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

                                Есть и второй способ уйти на низкий уровень. Пул потоков, и собственные примитивы синхронизации.
                                  Цитата Pavia @
                                  Критические секции ведут к дедлокам. Спинлоки в этом плане как-то по понятие. Но тоже народ тоже не задумывается о дедлоках. Поэтому они тоже приводят к дедлокам.
                                  Если делать всё грамотно, никаких дедлоков не будет. Менеджер памяти использует критические секции и ничего не лочится.
                                  К тому же, дедлоки происходят тогда, когда используется минимум 2 критические секции (в смысле, объекты - мьютексы).
                                    Цитата Pavia @
                                    Но лично я решаю проблемы архитектурно шаблон генерал-подчинённый. Там дедлоки исключены.
                                    У каждого потока свои данные, а передачу данных от одного потока к другому осуществляет генерал.
                                    За графику и окошки отвечает один поток. - пользователь у нас один ему хватит и одного потока. А вот обработку данных делают разные потоки.
                                    Не понял как это работает.
                                    Вот у потока с GDI есть всякие списки, свойства и пр. Нужно 2-м потокам изменить одновременно эти данные. Что для этого делается?
                                      Цитата Jin X @
                                      Вот у потока с GDI есть всякие списки, свойства и пр. Нужно 2-м потокам изменить одновременно эти данные. Что для этого делается?

                                      Что есть "поток с GDI"?

                                      В целом поверьте человеку, наевшемуся с потоками. Во избежание слома мозга из-за таинственных глюков по причине неучстенного совместного доступа к памяти или дедлоков взаимодействие между потоками надо максимально развязывать (см. PostThreadMessage). Данные предпочтительнее копировать, нежели блокировать. Если же блокировок не избежать, они д.б. как можно более короткими.
                                        Цитата Fr0sT @
                                        Что есть "поток с GDI"?
                                        Я хочу понять принцип "генерал-подчинённый".
                                        Есть 2 потока, им обоим нужно получить доступ к каким-либо данным.

                                        Цитата Fr0sT @
                                        В целом поверьте человеку, наевшемуся с потоками.
                                        Ты про Delphi конкретно?
                                        Если да, то вопросов нет - тут всё понятно.
                                          Цитата Jin X @
                                          Я хочу понять принцип "генерал-подчинённый".
                                          Есть 2 потока, им обоим нужно получить доступ к каким-либо данным.

                                          "принцип "генерал-подчинённый"" это очень расплывчато. Предполагаю, в данном случае это означает, что один поток является "владельцем" данных (может манипулировать ими в любой момент), а остальным позволяется получать к ним доступ только по сигналу.
                                          Подходы есть разные. Есть slim reader-writer (как в виде winapi объекта, так и эмуляцией через две крит. секции), когда кол-во одновременно читающих потоков не ограничено. Есть принцип Go - никаких разделяемых данных, строго копирование. Есть классические крит. секции.
                                          Цитата Jin X @
                                          Ты про Delphi конкретно?

                                          Да нет, это любого языка касается (ну кроме Go, там по-другому и не сделаешь)
                                            Цитата Fr0sT @
                                            Да нет, это любого языка касается
                                            Ну это высокоуровневые проблемы. С WinAPI же вроде как таких проблем нет...

                                            Цитата Fr0sT @
                                            Есть slim reader-writer (как в виде winapi объекта, так и эмуляцией через две крит. секции)
                                            Я думаю, что Pavia что-то другое имел в виду, т.к. ридером может быть и основной поток.

                                            Цитата Fr0sT @
                                            Есть принцип Go - никаких разделяемых данных, строго копирование.
                                            Что значит "копирование" в данном случае? Чтение? А как же запись?
                                              Цитата Jin X @
                                              С WinAPI же вроде как таких проблем нет...

                                              Такие проблемы есть везде, где есть потоки и разделяемые области памяти. Хоть на ассемблере
                                              Цитата Jin X @
                                              Что значит "копирование" в данном случае? Чтение? А как же запись?

                                              Копирование - это копирование. Записи никакой не предусмотрено. Надо передать потоку данные - отправляй копию.
                                              Жёстко, но полностью исключает проблемы совместного доступа
                                                Цитата Fr0sT @
                                                Копирование - это копирование. Записи никакой не предусмотрено. Надо передать потоку данные - отправляй копию.
                                                Жёстко, но полностью исключает проблемы совместного доступа
                                                А что с записью? Тоже через посредника?

                                                Цитата Fr0sT @
                                                Такие проблемы есть везде, где есть потоки и разделяемые области памяти.
                                                Давай пример при работы с GDI через WinAPI из 2-х потоков одновременно. Но чтобы косяк был не в коде (типа обработчика сообщений, который пишет куда-то).
                                                  Цитата Jin X @
                                                  А что с записью? Тоже через посредника?

                                                  Записью чего? Куда? Откуда? Данные существуют только в одном экземпляре, у владельца. Передача через копирование. Что непонятного?
                                                  Цитата Jin X @
                                                  Давай пример при работы с GDI через WinAPI из 2-х потоков одновременно. Но чтобы косяк был не в коде (типа обработчика сообщений, который пишет куда-то).

                                                  Разговор зашел не в ту степь. Есть вопрос - спрашивай конкретно, давать какие-то примеры для доказательства непонятно чего мне совсем не вперлось
                                                    Цитата Fr0sT @
                                                    Записью чего? Куда? Откуда? Данные существуют только в одном экземпляре, у владельца. Передача через копирование. Что непонятного?
                                                    Речь начиналась о критических секциях, насколько я помню.
                                                    Кто-то читает данные, а кто-то их пишет. Если чтение производится через посредника путём передачи копии, то как в этом механизме происходит запись этих данных?

                                                    Цитата Fr0sT @
                                                    Разговор зашел не в ту степь. Есть вопрос - спрашивай конкретно, давать какие-то примеры для доказательства непонятно чего мне совсем не вперлось
                                                    Причём тут доказательство? Я хочу понять что ты имеешь в виду: в каких случаях (например) возможны проблемы при обращении к WinAPI (GDI) одновременно двух потоков?
                                                      Цитата Jin X @
                                                      Если чтение производится через посредника путём передачи копии, то как в этом механизме происходит запись этих данных?

                                                      Так же как и чтение. Потоку-владельцу присылается копия данных, он уже решает, что с ними делать
                                                      Цитата Jin X @
                                                      Я хочу понять что ты имеешь в виду: в каких случаях (например) возможны проблемы при обращении к WinAPI (GDI) одновременно двух потоков?

                                                      Да легко. Попробуй рисовать на канвасе из двух потоков одновременно.
                                                      Многие объекты в winapi thread-safe, но не все.
                                                        Цитата Jin X @
                                                        в каких случаях (например) возможны проблемы при обращении к WinAPI (GDI) одновременно двух потоков?

                                                        Чего ты привязался к этому "WinAPI (GDI)"? Конкретизируй вопрос.
                                                        Первоначально речь шла только о показе MessageBox из доп.потока. Тут "проблемы" могут быть только в логике организации взаимодействия с юзером. Например, блокируешь ты основную форму на время показа MessageBox или нет. При hWnd=0 (как у тебя в примере #1) форма не блокируется и оба окна ведут себя независимо, поэтому весь расчет на то, что разумный юзер должен сначала закрыть МБ прежде чем тыкать кнопки\меню на форме. Но иногда возможны нештатные ситуации, когда в результате "не пойми чего" форма вылезает на передний план, закрывая МБ, и юзер (не замечая второго окна приложения на панели задач) начинает тыкать по форме, а МБ продолжает "благополучно висеть" под формой вместе со своим потоком.
                                                        Если же блокировать форму на время показа МБ (передавая ее hWnd в МБ), то ситуация становится более понятной\предсказуемой, но только в том случае, если показывается только один МБ. Если же у тебя несколько потоков могут одновременно показывать МБ (или не дай бог основной поток выдаст ShowMessage), то ситуация может быть еще более запутанной\непредсказуемой. Видимо "один товарищ" (упомянутый в посте #7) это и имел ввиду, а не то, что возможны какие-то критические ситуации с дедлоками, AV и т.п.

                                                        Добавлено
                                                        Цитата Fr0sT @
                                                        Да легко. Попробуй рисовать на канвасе из двух потоков одновременно.

                                                        На разных канвасах (оконных DC) - "легко". Винда без проблем отрисовывает десятки перекрывающихся окон, даже если они пытаются рисовать себя полностью, а не только видимую часть - все излишние "художества" благополучно вырезаются и на экран выводится общая картинка в соответствии с Z-порядком отображения окон.
                                                          Цитата leo @
                                                          Но иногда возможны нештатные ситуации, когда в результате "не пойми чего" форма вылезает на передний план, закрывая МБ, и юзер (не замечая второго окна приложения на панели задач) начинает тыкать по форме, а МБ продолжает "благополучно висеть" под формой вместе со своим потоком.
                                                          На этот случай есть MB_TASKMODAL, который я комбинирую с MB_SETFOREGROUND.

                                                          Цитата leo @
                                                          Если же блокировать форму на время показа МБ (передавая ее hWnd в МБ), то ситуация становится более понятной\предсказуемой, но только в том случае, если показывается только один МБ. Если же у тебя несколько потоков могут одновременно показывать МБ (или не дай бог основной поток выдаст ShowMessage), то ситуация может быть еще более запутанной\непредсказуемой
                                                          Такой задачи-то и нет :). Всё надо с умом делать :)

                                                          Цитата leo @
                                                          Видимо "один товарищ" (упомянутый в посте #7)
                                                          "Один товарищ", как выяснилось имел в виду ShowMessage, хотя разговор явно шёл про MessageBox. Ну перепутал он :)

                                                          Цитата Fr0sT @
                                                          Да легко. Попробуй рисовать на канвасе из двух потоков одновременно.
                                                          Многие объекты в winapi thread-safe, но не все.
                                                          Так, а что будет-то? Мне прямо даже интересно :)
                                                          И где-то вообще есть инфа о не-thread-safe-функциях?
                                                            Цитата leo @
                                                            На разных канвасах (оконных DC) - "легко". Винда без проблем отрисовывает десятки перекрывающихся окон, даже если они пытаются рисовать себя полностью, а не только видимую часть - все излишние "художества" благополучно вырезаются и на экран выводится общая картинка в соответствии с Z-порядком отображения окон.

                                                            Хитрый какой! )) Нет, я имел в виду на одном канвасе.
                                                            0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                                            0 пользователей:


                                                            Рейтинг@Mail.ru
                                                            [ Script execution time: 0,0704 ]   [ 19 queries used ]   [ Generated: 18.04.24, 03:36 GMT ]