Тупик при вызове потока пользовательского интерфейса из рабочего потока

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

return (ucAvancementTrtFamille)mInterfaceTraitement.Invoke(d, new object[] { psFamille });

Странно то, что поток пользовательского интерфейса (который, поправьте меня, если я ошибаюсь, является основным потоком) простаивает.

Есть ли способ:

  1. посмотреть, какой поток я на самом деле пытаюсь вызвать?
  2. посмотреть, что на самом деле делает указанный поток?

На изображении ниже мы видим, что рабочий поток (ID 3732) заблокирован на линии Invoke, а MainThread простаивает в основной функции приложения.

альтернативный текст

Изменить: вот стек основного потока:

альтернативный текст

Edit2: На самом деле, я приостановил программу во второй раз, и вот как выглядит стек:

альтернативный текст

Edit3: Найден обходной путь

Наконец-то я нашел обходной путь. Проблема, по-видимому, связана с проблемой состояния гонки асинхронной оболочки. Обходной путь — использовать BeginInvoke и ждать его с тайм-аутом. Когда время ожидания истечет, вызовите его снова и выполните цикл, пока он, наконец, не вернется. В большинстве случаев это действительно работает со второго звонка.

IAsyncResult ar = mInterfaceTraitement.BeginInvoke(d, new object[] { psFamille });
            while (!ar.AsyncWaitHandle.WaitOne(3000, false))
            {
                ar = mInterfaceTraitement.BeginInvoke(d, new object[] { psFamille });
            }
            // Async call has returned - get response
            ucAvancementTrtFamille mucAvancementTrtFamille = (ucAvancementTrtFamille)mInterfaceTraitement.EndInvoke(ar);

Это некрасиво, но это единственное решение, которое я нашел.


person leo    schedule 21.09.2010    source источник
comment
Можете ли вы опубликовать, что делает делегат d?   -  person Henk Holterman    schedule 21.09.2010
comment
И это приложение WinForms или WPF, верно?   -  person Henk Holterman    schedule 21.09.2010
comment
Делегат просто создает графический элемент управления (на данный момент он даже не добавляет его ни в какую форму). Я использую ВинФормс.   -  person leo    schedule 22.09.2010
comment
Есть ли вероятность того, что поток пользовательского интерфейса ожидает рабочий поток в любой момент?   -  person Joren    schedule 22.09.2010


Ответы (5)


Основной поток не выглядит бездействующим. На снимке экрана показано его текущее местоположение в ECM.Program.Main. Это не может быть правильным, если он простаивает, то он находится внутри Application.Run(), прокачивая цикл сообщений. Это необходимо для завершения Invoke().

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

person Hans Passant    schedule 21.09.2010
comment
Ну, вы правы: это в Application.Run(), который находится в моем ECM.Program.Main. Смотрите мое редактирование: он показывает стек основного потока. - person leo; 22.09.2010
comment
Да, похоже, меня это тоже качает. Грубый. Настройте сервер символов, включите отладку неуправляемого кода и покажите нам стек вызовов заблокированного потока. - person Hans Passant; 22.09.2010
comment
Похоже, что у асинхронной оболочки есть проблема с состоянием гонки. См. мое третье редактирование с найденным обходным путем. - person leo; 23.09.2010

Вы пробовали использовать BeginInvoke вместо Invoke? BeginInvoke является асинхронным.

person Chris Laplante    schedule 21.09.2010
comment
Да, но BeginInvoke не блокирует звонящего. Это просто предложение, хотя да, это не должно иметь значения. - person Chris Laplante; 21.09.2010
comment
SimpleCoder, OP хочет вернуть значения из Invoke. BeginInvoke не может просто заменить здесь. - person Henk Holterman; 21.09.2010

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

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

Если ваш рабочий поток действительно заблокирован при вызове Control.Invoke, а поток пользовательского интерфейса простаивает, как вы утверждаете, проблема может быть связана с выполнением инструкций в делегате, который маршалируется, или циклом обработки сообщений, поскольку он еще не запущен. Последнее кажется правдоподобным, так как ваш экран показывает расположение основного потока как Main.

person Brian Gideon    schedule 21.09.2010
comment
Я редактирую свой вопрос и добавляю стек основного потока. Я включил опцию Показать внешний код. - person leo; 22.09.2010

Вы пытались вместо этого использовать BackgroundWorker. Если вы используете его, это сэкономит вам много вызовов this.Invoke и InvokeRequired.
Я считаю, что если вы создадите новый поток и заставите его выполнять строку, которую вы показываете, у вас не будет взаимоблокировки. Я постараюсь найти свой старый код и опубликовать его здесь.

person sh_kamalh    schedule 21.09.2010

Вы используете Visual Studio 2008? Если да, попробуйте Visual Studio 2010. Это известная ошибка.

Удачи !

person Bastien D    schedule 22.09.2010