Control.Invoke застревает в скрытом ShowDialog - PullRequest
4 голосов
/ 13 января 2010

(У меня есть обходной путь для этой проблемы, но я не первый раз укушен, поэтому я пытаюсь точно понять, что происходит.)

  • Из моего заявления я ShowDialog форма.
  • На форме есть кнопка, которая при нажатии вызывает код в другом (не-Gui) потоке.
  • Поток без графического интерфейса отправляет назад статусы (Pushed затем Released) через Control.Invoke
  • Когда форма видит Pushed, она вызывает form.Hide()
  • Когда форма видит Released, она меняет внешний вид кнопки.

Случается так, что иногда, но не каждый раз, не-Gui-поток «застревает» при попытке отправить Released. Без исключений, Gui продолжает «работать», но дальнейшее общение с нитью не-Gui невозможно в любом направлении.

(упрощенный) стек вызовов для потока выглядит следующим образом:

System.Threading.WaitHandle.WaitOne()
(...)
System.Windows.Forms.Control.WaitForWaitHandle()
(...)
System.Windows.Forms.Control.Invoke()
(...)
GuiCode.OnStatusChanged()
(...)
NonGuiCode.SetStatus()

Проблема исчезнет, ​​если я заменим ShowDialog на Show, но - что интересно - она ​​станет лучше (случается реже), но не исчезнет полностью, если я закомментирую код, который выполняет Hide Pushed.

Обновление

Благодаря nobugz я обнаружил тупик (раньше я встречал его только в базах данных)! Очевидно, замена Control.Invoke на Control.BeginInvoke решает эту проблему (событие состояния все еще иногда «застревает», но не блокирует все последующие сообщения).

Ответы [ 3 ]

4 голосов
/ 29 июля 2010

Для обработки вызова Control.Invoke() поток GUI должен обработать сообщение Windows, но ShowDialog() является блокирующим вызовом, поэтому он не может этого сделать, пока не вернется ShowDialog().

Control.Invoke() также блокируется, и поток, вызывающий его, должен ждать, пока поток GUI не заберет сообщение и не обработает его, чтобы продолжить. Если код, включающий Control.Invoke(), позволяет закрыть диалоговое окно, то bingo , значит, у вас тупик.

Все это немного сложно, потому что детекторы взаимоблокировок, такие как команда dlk SosEx, не могут обнаружить проблему из сеанса дампа или WinDgb - «блокировка», вовлеченная в обработку потока GUI для Control.Invoke(), подразумевается не фактический WaitHandle.

3 голосов
/ 13 января 2010

Очевидно, вы сражаетесь в тупик. Это всегда не за горами, когда вы используете Control.Invoke () вместо BeginInvoke (). Мне непонятно, из-за чего зашел твой тупик. Один красный флаг использует Hide () на форме, которую вы отображали с помощью ShowDialog (). Это обычно закрывает диалог.

Лучше всего отладить его. Подождите, пока не возникнет взаимоблокировка, затем используйте Debug + Break All. Используйте Debug + Windows + Threads и переключитесь на поток пользовательского интерфейса. Посмотрите на стек вызовов. Если он не перекачивает цикл обработки сообщений (Application.Run ()), но где-то застрял (например, дескриптор ожидания, который вы, похоже, используете), то результатом является тупик.

0 голосов
/ 16 февраля 2010

Я только что столкнулся с тем, что я думаю, это.

Из потока GUI вызовите другой поток GUI, и пусть этот поток выполняет ShowDialog. Если настройки пользовательского интерфейса пользователя изменяются (например, поворот фона), тупик.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...