Пересылка данных MFC в основной поток через PostMessage - PullRequest
5 голосов
/ 28 сентября 2010

У меня есть приложение на C ++ / MFC, которое мне нужно реструктурировать.Приложение раньше обрабатывало большую часть данных в основном потоке, поэтому блокировало ввод, и теперь я хочу изменить его так, чтобы все обновления графического интерфейса выполнялись через PostMessage.

К сожалению, я не могу показатьсянайти хороший источник информации о том, как достичь этой цели.

Сейчас я думаю о создании очереди с приоритетами, защищенной критическим разделом, рабочим потоком (while (true)), который обрабатывает эту очередь, иМеханизм PostMessage, который отправляет указатели на данные в основной поток.

Меня пугает такой подход, так как PostMessage вообще не гарантированно достигает основного потока, поэтому, если я правильно понимаю, есть шансутечка памяти.

Вторая проблема заключается в том, что другое приложение может отправить пользовательское сообщение в мое приложение, и мое приложение может попытаться разыменовать WPARAM или LPARAM в качестве указателя, в результате чего AV.

Кто-нибудь знает, каковы лучшие практики для таких задач?

Данные могут быть HTML-контентом для веб-контроля,или другой контент для списков, выпадающих списков и т. д.

Ответы [ 3 ]

11 голосов
/ 28 сентября 2010

Ваши сообщения будут там.Я не уверен, почему вы думаете, что PostMessage не гарантированно работает - это так.(РЕДАКТИРОВАТЬ: Предполагая, что PostMessage () возвращает TRUE! Проверьте ваши коды возврата!)

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

Вместо этого создайте структуру данных в куче, используя new, которая содержит ваши данные, затем скажите другому потоку: «У меня есть данные для вас, ивот."Затем принимающий поток становится владельцем этого указателя данных и отвечает за его использование.Делая это таким образом, нет жестких замков.

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

Если вы отправляете данные из рабочего потока в основной поток, просто используйте PostMessage():

worker_thread_proc()
{
// ..

  // Create the data object you're going to pass to the MT
  MyData* data = new MyData;
  data->some_value_ = "foo";

  // Pass it:
  PostMessage(main_wnd, WM_YOU_HAVE_DATA, reinterpret_cast<WPARAM>(data), 0);
}

... основной поток обрабатывает это, а затем удаляет данные:

MainWnd::OnYouHaveData(WPARAM wp, LPARAM)
{
  std::auto_ptr<MyData> data(reinterpret_cast<MyData*>(wp));
  my_widget->set_text(data->some_value_); // you get the idea
}

Если вы беспокоитесь о пользовательских сообщениях внешних приложений, сталкивающихся с вашими, вы можете заставить Windows дать вам уникальный идентификатор сообщения, используя RegisterWindowsMessage () - ваша единственная задачаздесь выбирается правильное имя для вашего сообщения.

Если вы отправляете данные из основного потока в рабочий поток, вы можете сделать то же, что и выше, за исключением того, что вместо использования PostMessage() вы отправляете данные черезВы можете использовать QueueUserAPC () (чтобы убедиться, что ваш рабочий находится в состоянии ожидания с предупреждением - прочитайте примечания в связанных документах) или PostThreadMessage () .

РЕДАКТИРОВАТЬ:

По вашим комментариям в OP, теперь я понимаю, почему вы обеспокоены тем, что PostMessage () не работает.

Да, существует жесткое ограничение для Windowsразмер очереди сообщений.По умолчанию в очереди может быть только 4000 сообщений.(в настройках реестра можно настроить максимум до 10 000).

Если очередь заполнена, любой вызов PostMessage() завершится ошибкой с кодом ошибки.Когда вы проверяете GetLastError () (я не помню, какой код ошибки он возвращает прямо сейчас), станет ясно, что очередь сообщений заполнена.

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

2 голосов
/ 28 сентября 2010

Используйте две очереди, одну для рабочих запросов, идущих в рабочий поток, и одну для результатов, возвращающихся в основной поток.Вы можете использовать PostMessage, чтобы разбудить основной поток и сказать ему, чтобы он проверял очередь, но в сообщении вам не понадобятся никакие параметры.

1 голос
/ 28 сентября 2010

Я решил похожую проблему некоторое время назад.Я создал одноэлементную очередь для хранения данных (или действий), которые должны перетекать из фоновых потоков в основной поток.Очередь защищена критическим разделом.Фоновый поток помещает свои данные в очередь и публикует сообщение.Сообщение не содержит никаких данных, оно действует как простой сигнал пробуждения: «Эй, главный поток, посмотри на очередь, у тебя есть работа».

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

...