2015-04-11 2 views
1

Я знаю, что для доступа к элементам управления пользовательского интерфейса из другого потока я должен использовать PostMessage(). Однако PostMessage() является асинхронным, поэтому, например, если я попытаюсь изменить текст элемента управления "EDIT", я не смогу удалить текстовый буфер, когда закончите, потому что я не знаю, когда оконная процедура завершит обработку сообщения.Как получить доступ к элементам управления пользовательского интерфейса из другого потока?

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

  • Создайте определенное пользователем сообщение, например, я посылаю к UI нить что-то вроде: "вставки эти 200 строк (все хранятся в одной строке, разделенные символом «\ n» или что-то еще) в ListViewX », и когда поток пользовательского интерфейса получает это сообщение, он обновляет ListViewX, и по завершении удаления строки (которая выделяется на куча).
  • Другой подход состоит в том, чтобы поместить код, который обращается к элементу управления пользовательского интерфейса внутри функции, и отправить указатель на поток пользовательского интерфейса, который затем вызовет его.

Имеет ли один из этих подходов некоторые преимущества перед другими, и существуют ли другие подходы к этому?

+0

Если допустимо, чтобы поток блокировался до тех пор, пока поток пользовательского интерфейса не обработал сообщение, «SendMessage» - это еще один вариант. –

+0

@Harry Johnston Я прочитал, что 'SendMessage()' может вызвать тупик (что не всегда легко предсказать). См .: http://flounder.com/workerthreads.htm, раздел: ** Рабочие потоки и графический интерфейс II: не трогайте GUI **. – user4582812

+1

Я не согласен с интерпретацией этой статьи этой статьи. Тупик произошел из-за того, что поток GUI ожидал завершения рабочего потока и реализовал ожидание таким образом, что сообщения GUI не обрабатывались в то же время. Вы не должны этого делать, потому что даже если вы не зашли в тупик, графический интерфейс будет заморожен до тех пор, пока рабочий поток не завершится. Отказ от ответственности: я не являюсь экспертом в программировании GUI. –

ответ

3

Я часто использую следующий шаблон (стрелки указывают на «использует» отношения):

  +---------------+      
      | Communication |  +-----------+ 
     +--->+ Data Object +<---+---+ Thread #0 | 
     | | (thread safe) | | +-----------+ 
     | +---------------+ +---+ Thread #1 | 
     |       | +-----------+ 
     |       |  ...   
+-----+----+     | +-----------+ 
| Main (UI)|     +---+ Thread N | 
| Thread |      +-----------+ 
+----------+          

Объект Communication Data является поточно и обычно ref-counted (важно для не-GC-языков). Объект обеспечивает несколько типичных методов (все по желанию, в зависимости от фактического случая использования):

  • очереди рабочий элемент
  • данные геттер/сеттер для любых других данных, которые необходимо обмениваться информацией
  • прогресс чтобы быть визуализированы в пользовательском интерфейсе
  • флаг прерывания/состояние, в сочетании с ...
  • waitable объекта события, чтобы сигнализировать различные вещи

Поскольку объект данных полностью заботится о себе, включая синхронизацию доступа к его данным и более или менее автоматическую очистку из-за подсчета количества ссылок, вы получаете весь этот материал потока, который удачно инкапсулирован в один выделенный класс. Я нашел это очень удобным в различных ситуациях.

Кроме того, подход полностью отделяет рабочие потоки от UI, поэтому потоки даже не знают, есть ли пользовательский интерфейс и как он выглядит: это графический интерфейс? Это CLI? Может быть, веб-сервис? Последнее, что не в последнюю очередь, поддерживает пользовательский интерфейс, поскольку поток пользовательского интерфейса (или его эквивалент) может полностью решить сам по себе, когда и как часто обновлять пользовательский интерфейс.


PS: Возможно, есть официальное название GOF для него, не знаю.

+0

Просто указывая на различие между двумя подходами: один из способов заключается в том, что рабочий поток блокируется до тех пор, пока запрос не попадет в основной поток пользовательского интерфейса и обратно; а другой - «огонь и забыть», где объектом связи будет потокобезопасный FIFO или сообщение Windows, которое не ожидает ответа –

+0

Это не FIFO как таковой. Это может быть все, что вы хотите, включая FIFO, но не ограничиваясь этим. Наиболее важным моментом здесь является развязка, в частности рабочие потоки не знают о пользовательском интерфейсе. Представьте, что вы хотите использовать эту часть кода в серверном приложении, например. веб-службы. Нет 'HWND' для' SendMessage() 'to. Но поскольку мы отделили все это, вам просто нужно написать часть из потока, который будет потоком пользовательского интерфейса в настольном приложении, а также ждать блокировки в любом месте. – JensG

1

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

Вы ошибаетесь. Нет никакого принуждения использовать PostMessage. И действительно, для установки текста окна элемента управления вы не должны использовать PostMessage. Вам нужно отправить синхронно WM_SETTEXT, по тем причинам, которые вы наброскаете. Если вы не отправляете его синхронно, вы не знаете, когда нужно уничтожить текстовый буфер.

Что вам нужно сделать, это выглядит следующим образом:

  • Если окно находится в вашем процессе, то вы должны использовать SetWindowText.
  • Если окно находится в другом процессе, то вы должны использовать SendMessageTimeout для отправки сообщения WM_SETTEXT.

Для окон в другом процессе SetWindowText документально не работает. Это немного сложнее, чем Raymond explains, но вы, как правило, не должны использовать его в окне в другом процессе. Итак, для окон в другом процессе используйте SendMessageTimeout для отправки WM_SETTEXT. Тайм-аут состоит в том, чтобы предотвратить зависание приложения, если приложение установлено.

+0

«Если вы не отправляете его синхронно, тогда вы не знаете, когда уничтожить текстовый буфер» Я использую 'PostMessage()' для отправки пользовательского сообщения, а не для отправки 'WM_SETTEXT', а в пользовательском интерфейсе thread Я вызываю 'SendMessage()' с 'WM_SETTEXT', и когда' SendMessage() 'возвращает, я уничтожаю текстовый буфер. – user4582812

+0

"* Я использую PostMessage() для отправки пользовательского сообщения *" - отлично, если параметры сообщения не содержат указатели, которые нуждаются в очистке. Если это так, у вас опять такая же проблема, только с другим идентификатором сообщения. 'PostMessage()' является асинхронным, что вызывает проблему того, кто очищает когда. А что, если окно уйдет, и никто не получит сообщение? Утечка памяти? Это трудно справиться и поддерживать, а также подвержено ошибкам. Я бы этого не сделал. – JensG

+0

@JensG Я отправляю указатель на буфер в пользовательском сообщении, а затем я вызываю 'SendMessage()' из потока пользовательского интерфейса, а затем удаляю буфер, когда 'SendMessage()' возвращает. Я не понимаю, как это не сработает! Что касается окна, которое уничтожается до того, как будет обработано сообщение, этого не произойдет, поскольку я отправляю пользовательские сообщения в главное окно (так что единственный способ его уничтожения - закрыть приложение). – user4582812

Смежные вопросы