Подтвердить что ты не робот

Альтернатива для сообщений и отправки сообщений

У меня есть программа, которая использует несколько потоков для выполнения некоторых задач. Каждый поток имеет кучу задачи для выполнения. после выполнения одного из них каждый thred вызовет сообщение сообщения на главный экран для обновления журналов.

Теперь у меня шестьдесят тысяч заданий по десять тысяч для каждого потока - шесть потоков - после выполнения каждой задачи поток вызывает сообщения сообщений. но из-за этих сообщений сообщения мое приложение становится очень занятым и выглядит как повешено.

если я удаляю сообщения сообщений... все отлично работает. Но я не могу вызвать процедуру напрямую, потому что она использует элементы управления ui, а элементы управления ui не являются потокобезопасными, а процедура вызова непосредственно из потока приведет к другим ошибкам.

есть ли альтернатива, доступная для postmessage и отправки сообщения.

Спасибо, Базилик

4b9b3361

Ответ 1

Проблема заключается в том, что есть две очереди сообщений для сообщений. Результатом этого является то, что ваши опубликованные сообщения всегда обрабатываются перед любыми сообщениями Paint, Input или Timer.

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

Общим способом решения этой проблемы является использование таймера; чтобы ваш код начинал очень короткий (например, 0 мс) таймер.

Разъяснение

Сообщения о таймере (WM_TIMER), такие как сообщения Paint (WM_PAINT) и входные сообщения (например, WM_MOUSEMOVE, WM_KEYDOWN) обрабатываются после отправленных сообщений. Специальные сообщения таймера обрабатываются после ввода и сообщений краски.

Это означает, что ваше приложение будет отвечать на пользовательские события и писать запросы до того, как оно обработает следующее сообщение WM_TIMER. Вы можете использовать это поведение, используя сообщение "Таймер" (и это сообщение WM_TIMER), вместо того, чтобы публиковать сообщение самостоятельно (что всегда будет иметь приоритет над красками и сообщениями ввода).

При срабатывании таймера они отправляют сообщение WM_TIMER. Это сообщение всегда будет обрабатываться после любых сообщений ввода и рисования; что делает ваше приложение более отзывчивым.

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

Бонус-чат

... сообщения обрабатываются в следующем порядке:

  • Отправленные сообщения
  • Отправленные сообщения
  • Вводные (аппаратные) сообщения и внутренние события системы
  • Отправленные сообщения (снова)
  • сообщения WM_PAINT
  • сообщения WM_TIMER

Обновить. Вы должны не иметь опрос таймера, который просто расточительный и неправильный. Ваши потоки должны "установить флаг" (т.е. Таймер). Это позволяет вашему основному потоку фактически простаивать, только бодрствуя, когда есть что-то делать.

Обновить два:

Псевдокод, демонстрирующий, что порядок генерации сообщений не сохраняется:

//Code is public domain. No attribution required.
const
  WM_ProcessNextItem = WM_APP+3;

procedure WindowProc(var Message: TMessage)
begin
   case Message.Msg of
   WM_Paint: PaintControl(g_dc);
   WM_ProcessNextItem:
      begin
          ProcessNextItem();
          Self.Invalidate; //Invalidate ourselves to trigger a wm_paint

          //Post a message to ourselves so that we process the next 
          //item after any paints and mouse/keyboard/close/quit messages
          //have been handled
          PostMessage(g_dc, WM_ProcessNextItem, 0, 0); 
      end;
   else
      DefWindowProc(g_dc, Message.Msg, Message.wParam, Message.lParam);
   end;
end;

Даже если я создаю WM_ProcessNextItem после того, как я сгенерирую сообщение WM_PAINT (т.е. Invalidate), WM_PAINT никогда не будет обработан, потому что перед ним всегда есть другое сообщение. И как сообщает MSDN, сообщения с краской появятся, только если нет других опубликованных сообщений.

Обновить три. Да, есть только очередь сообщений, но вот почему нам все равно:

Отправленные и отправленные сообщения

Терминология, которую я буду использовать здесь, нестандартна, но я использую ее, потому что я думаю, что она немного понятна, чем стандартная терминология. Для этой дискуссии я собираюсь сказать, что сообщения, связанные с потоком, делятся на три ковши, а не на более стандартные:

What I'll call them            Standard terminology
===========================    =============================
Incoming sent messages         Non-queued messages
Posted messages            \_  
Input messages             /   Queued messages

В действительности, распад сообщений более сложный, чем этот, но на данный момент мы придерживаемся вышеуказанной модели, потому что это "правда достаточно".

Старая новая вещь, практическое развитие на протяжении всей эволюции Windows
Раймонд Чен
ISBN 0-321-44030-7
Copyright © 2007 Pearson Education, Inc.
Глава 15 - Как выводить и восстанавливать сообщения окна, Страница 358

Легче представить, что есть две очереди сообщений. Никакие сообщения в "втором" не будут прочитаны, пока "первая" очередь не будет пуста; и ОП никогда не пропускает первую очередь. В результате все сообщения "краска" и "enter" во второй очереди обрабатываются, что приводит к зависанию приложения.

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

Обновить четыре

Проблема не в том, что вы "наводняете" входную очередь своими сообщениями. Ваше приложение может быть невосприимчивым только с одним сообщением. Пока у вас есть одно отправленное сообщение в очереди, оно будет обработано перед любым другим сообщением.

Представьте, что произошла серия событий:

  • Мышь перемещается (WM_MOUSEMOVE)
  • Левая кнопка мыши нажата вниз (WM_LBUTTONDOWN)
  • Отключена левая кнопка мыши (WM_LBUTTONUP)
  • Пользователь перемещает другое окно в сторону, заставляя ваше приложение перерисовываться (WM_PAINT)
  • В вашей ветке есть готовый элемент и отправляет уведомление (WM_ProcessNextItem)

Ваше приложение основной цикл сообщений (который вызывает GetMessage) не получит сообщения в том порядке, в котором они были. Он получит сообщение WM_ProcessNextItem. Это удаляет сообщение из очереди, оставляя:

  • WM_MOUSEMOVE
  • WM_LBUTTONDOWN
  • WM_LBUTTONUP
  • WM_PAINT

Пока вы обрабатываете свой элемент, пользователь перемещает мышь еще немного и нажимает случайно:

  • WM_MOUSEMOVE
  • WM_LBUTTONDOWN
  • WM_LBUTTONUP
  • WM_PAINT
  • WM_MOUSEMOVE
  • WM_MOUSEMOVE
  • WM_MOUSEMOVE
  • WM_MOUSEMOVE
  • WM_LBUTTONDOWN
  • WM_LBUTTONUP
  • WM_MOUSEMOVE
  • WM_MOUSEMOVE
  • WM_LBUTTONDOWN
  • WM_LBUTTONUP
  • WM_MOUSEMOVE
  • WM_MOUSEMOVE
  • WM_LBUTTONDOWN
  • WM_LBUTTONUP

В ответ на ваш WM_ProcessNextItem вы отправляете другое сообщение себе. Вы делаете это, потому что сначала хотите обработать выдающиеся сообщения, прежде чем продолжить обработку большего количества элементов. Это добавит еще одно сообщение в очередь:

  • WM_MOUSEMOVE
  • WM_LBUTTONDOWN
  • WM_LBUTTONUP
  • WM_PAINT
  • WM_MOUSEMOVE
  • WM_MOUSEMOVE
  • WM_MOUSEMOVE
  • WM_MOUSEMOVE
  • WM_LBUTTONDOWN
  • WM_LBUTTONUP
  • WM_MOUSEMOVE
  • WM_MOUSEMOVE
  • WM_LBUTTONDOWN
  • WM_LBUTTONUP
  • WM_MOUSEMOVE
  • WM_MOUSEMOVE
  • WM_LBUTTONDOWN
  • WM_LBUTTONUP
  • WM_ProcessNextItem

Проблема начинает становиться очевидной. Следующий вызов GetMessage будет извлекать WM_ProcessNextItem, оставляя приложение с отставанием красок и входных сообщений:

  • WM_MOUSEMOVE
  • WM_LBUTTONDOWN
  • WM_LBUTTONUP
  • WM_PAINT
  • WM_MOUSEMOVE
  • WM_MOUSEMOVE
  • WM_MOUSEMOVE
  • WM_MOUSEMOVE
  • WM_LBUTTONDOWN
  • WM_LBUTTONUP
  • WM_MOUSEMOVE
  • WM_MOUSEMOVE
  • WM_LBUTTONDOWN
  • WM_LBUTTONUP
  • WM_MOUSEMOVE
  • WM_MOUSEMOVE
  • WM_LBUTTONDOWN
  • WM_LBUTTONUP

Решение состоит в том, чтобы воспользоваться обработкой сообщений вне очереди. Отправленные сообщения всегда обрабатываются до сообщений Paint/Input/Timer: не используйте отправленное сообщение. Вы можете думать о том, что очередь сообщений разделена на две группы: Отправленные сообщения и Вводные сообщения. Вместо того, чтобы вызывать ситуацию, когда очередь сообщений "Отправлено" никогда не разрешается пустым:

Posted messages      Input messages
==================   =====================
WM_ProcessNextItem   WM_MOUSEMOVE
                     WM_LBUTTONDOWN
                     WM_LBUTTONUP
                     WM_PAINT

Вы используете сообщение WM_TIMER:

Posted messages      Input messages
==================   =====================
                     WM_MOUSEMOVE
                     WM_LBUTTONDOWN
                     WM_LBUTTONUP
                     WM_PAINT
                     WM_TIMER

Nitpickers Corner. Это описание двух очередей не строго верно, но это достаточно верно. Как Windows доставляет сообщения в документированном порядке - это внутренняя деталь реализации, которая может быть изменена в любое время.

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

Использование таймера имеет другое преимущество использования. Между WM_PAINT, WM_TIMER и входящими сообщениями также выполняется обработка вне очереди:

  • ввод сообщений, затем
  • WM_PAINT, затем
  • WM_TIMER

Если вы используете таймер для уведомления основного потока, вы также можете гарантировать, что вы будете обрабатывать краску и ввод данных раньше. Это гарантирует, что ваше приложение останется отзывчивым. Это улучшение удобства использования, и вы получаете его бесплатно.

Ответ 2

Вы будете пытаться найти что-нибудь лучше, чем PostMessage. Я предполагаю, что ваша проблема в том, что вы обновляете интерфейс слишком часто, и ваша очередь становится насыщенной, потому что вы не можете ее обслуживать достаточно быстро. Как насчет пропуска обновлений, если вы обновили менее секунды назад? Если это восстановит отзывчивость, тогда вы можете рассмотреть более надежное решение.

Ответ 3

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

В основном потоке используйте событие таймера, чтобы слить очередь.

Чтобы оставаться отзывчивыми, не останавливайтесь слишком долго в событии таймера.

Во многих случаях я нашел это лучше, чем сообщение сообщений.

В Delphi XE существует класс, называемый TThreadedQueue, который вы можете использовать.

Изменить:

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

См. ThreadedQueueDemo

Ответ 4

Я делал это много раз. Попросите опубликованное сообщение из потоков обновить "кеш" в основной форме. У вас есть таймер на основной форме (установленный около 100 мс или меньше, если вам нужно), чтобы обновить основную форму из кеша. Таким образом, объем работы, выполненной для каждого отправленного сообщения, очень мал, поэтому приложение будет тратить меньше времени на обработку, и ваше приложение будет выглядеть "отзывчивым".

Ответ 5

Вы можете создать новый поток для обновления журналов и в потоках журналов вызвать TLogThread.Synchronize для обновления элементов управления пользовательского интерфейса, которые находятся в главном потоке приложения... или просто вызвать TWorkerThread.Synchronize для обновления журналов из ваших рабочих потоков.