Control.Invoke получает "застрял" в скрытом ShowDialog
(У меня есть обходной путь для этой проблемы, но это не первый раз, когда я был укушен, поэтому я пытаюсь понять, что происходит.)
- Из моего приложения я
ShowDialog
форма.
- В форме есть кнопка, которая при нажатии на код вызывает другой (не-Gui) поток.
- Нить без GUI отправляет состояния статуса (
Pushed
then 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
on Pushed
.
Обновление
Благодаря nobugz я обнаружил тупик (раньше я только встречался в базах данных)! По-видимому, замена Control.Invoke на Control.BeginInvoke решает эту проблему (событие состояния по-прежнему иногда "застревает", но не блокирует все последующие сообщения).
Ответы
Ответ 1
Ясно, что вы сражаетесь с тупиком. Это всегда за углом, когда вы используете Control.Invoke() вместо BeginInvoke(). Мне непонятно, что из тупика выйдет из тупика. Один красный флаг использует Hide() в форме, отображаемой с помощью ShowDialog(). Это обычно закрывает диалог.
Лучше всего это отладить его. Подождите, пока произойдет тупик, затем используйте Debug + Break All. Используйте Debug + Windows + Threads и переключитесь на поток пользовательского интерфейса. Посмотрите на стек вызовов. Если он не перекачивает контур сообщения (Application.Run()), а застревает где-то (например, дескриптор ожидания, который вы используете), то результатом является тупик.
Ответ 2
Чтобы обрабатывать вызов Control.Invoke()
, поток GUI должен передавать сообщение Windows, но ShowDialog()
является блокирующим вызовом, поэтому он не может этого сделать до тех пор, пока ShowDialog()
не вернется.
Control.Invoke()
также блокирует, и поток, вызывающий его, должен ждать, пока поток GUI не подберет сообщение и не продолжит его. Если код, включающий Control.Invoke()
, позволяет отключить диалог, тогда bingo, там ваш тупик.
Все это немного сложно, потому что тупиковые детекторы, такие как SosEx dlk
, не могут обнаружить проблему из сеанса дампа или WinDgb - блокировка, участвующая в обработке потоков GUI Control.Invoke()
, подразумевается ', а не фактический WaitHandle
.
Ответ 3
Я просто столкнулся с тем, что я думаю.
Из потока GUI вызовите другой поток GUI и создайте этот поток ShowDialog. Если пользовательские настройки GUI изменяются (например, ротатор фона), тупик.