WPF Dispatcher.Invoke "висит"
У меня есть несколько сложное приложение WPF, которое, кажется, "висит" или застревает в вызове "Ожидание" при попытке использовать диспетчера для вызова вызова в потоке пользовательского интерфейса.
Общий процесс:
- Обработать событие click нажатием кнопки
- Создайте новый поток (STA), который: создает новый экземпляр ведущего и пользовательского интерфейса, затем вызывает метод Отключить
- Отключить затем устанавливает свойство в пользовательском интерфейсе Имя
- Затем установщик для имени использует следующий код для установки свойства:
if(this.Dispatcher.Thread != Thread.CurrentThread)
{
this.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate{
this.Name = value; // Call same setter, but on the UI thread
});
return;
}
SetValue(nameProperty, value); // I have also tried a member variable and setting the textbox.text property directly.
Моя проблема заключается в том, что при вызове метода диспетчера invoke он, кажется, висит каждый раз, а callstack указывает, что он находится во сне, ждет или присоединяется к реализации Invoke.
Итак, есть ли что-то, что я делаю неправильно, я отсутствую, очевидно или нет, или есть лучший способ обратиться к потоку пользовательского интерфейса, чтобы установить это свойство (и другие)?
Изменить:. Решение заключалось в вызове System.Windows.Threading.Dispatcher.Run() в конце делегата потока (например, когда работа выполнялась) - Спасибо всем, кто помог.
Ответы
Ответ 1
Вы говорите, что вы создаете новый поток STA, является диспетчером этого нового потока?
Я получаю от "this.Dispatcher.Thread!= Thread.CurrentThread", что вы ожидаете, что он будет другим диспетчером. Убедитесь, что его выполнение в противном случае не будет обрабатывать свою очередь.
Ответ 2
Вызывать синхронно - вы хотите Dispatcher.BeginInvoke. Кроме того, я считаю, что ваш образец кода должен перемещать "SetValue" внутри оператора "else".
Ответ 3
Я думаю, что это лучше показано с кодом. Рассмотрим этот сценарий:
Тема А делает следующее:
lock (someObject)
{
// Do one thing.
someDispatcher.Invoke(() =>
{
// Do something else.
}
}
Thread B делает следующее:
someDispatcher.Invoke(() =>
{
lock (someObject)
{
// Do something.
}
}
На первый взгляд все может показаться прекрасным и денди, но его нет. Это приведет к тупиковой ситуации. Диспетчеры похожи на очереди для потока, и, когда они имеют дело с такими взаимоблокировками, важно подумать о них так: "Какая предыдущая отправка могла замять мою очередь?". Тема А войдет в... и отправится под замок. Но, что, если поток B входит в момент времени, когда Thread A находится в коде, отмеченном "Сделать одну вещь"? Ну...
- В потоке A есть блокировка на someObject и выполняется некоторый код.
- Отправляет поток B, и диспетчер попытается получить блокировку на someObject, забив ваш диспетчер, поскольку Thread A уже имеет эту блокировку.
- Затем поток A будет помещать в очередь другой элемент отправки. Этот элемент никогда не будет запущен, потому что ваш диспетчер никогда не завершит обработку вашего предыдущего запроса; его уже застряли.
Теперь у вас прекрасный тупик.
Ответ 4
Я думаю, вы имеете в виду, если (! this.Dispatcher.CheckAccess())
Я также получаю зависание с Invoke, или если я могу BeginInvoke, мой делегат не вызывается - кажется, все делает по книге: - (
Ответ 5
Это звучит как тупик; это обычно случается, если поток, вызывающий .Invoke, уже содержит блокировку /mutex/etc, которые должен выполнить поток пользовательского интерфейса, чтобы завершить работу. Самый простой подход - использовать BeginInvoke вместо этого: таким образом, текущий поток может продолжать работать, и он (предположительно) освободит блокировку в ближайшее время, позволяя пользовательскому интерфейсу реализовать его. В качестве альтернативы, если вы можете определить блокировку нарушения, вы можете намеренно отпустить ее на время.
Ответ 6
У меня есть аналогичная проблема, и пока я все еще не уверен, что ответ, я думаю, что ваш
if(this.Dispatcher.Thread != Thread.CurrentThread)
{
this.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate{
this.Name = value; // Call same setter, but on the UI thread
});
return;
}
следует заменить на
if(this.Dispatcher.CheckAccess())
{
this.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate{
this.Name = value; // Call same setter, but on the UI thread
});
return;
}
CheckAccess не будет отображаться в Intellisense, но он существует и предназначен для этой цели. Кроме того, я согласен с тем, что в общем вы хотите, чтобы BeginInvoke здесь, однако я обнаружил, что я не получаю обновления пользовательского интерфейса, когда я делаю это асинхронное. К сожалению, когда я делаю это синхронно, я получаю условие взаимоблокировки...
Ответ 7
Я знаю, что это старый поток, но вот еще одно решение.
Я просто исправил аналогичную проблему. Мой диспетчер работал нормально, поэтому...
Мне нужно было показать DEBUG → THREAD WINDOW, чтобы идентифицировать все потоки, выполняющие мой код где угодно.
Проверяя каждый из потоков, я быстро увидел, какой поток вызвал тупик.
Это были несколько потоков, объединяющие оператор lock (locker) { ... }
и вызывающие Dispatcher.Invoke().
В моем случае я мог просто изменить конкретный оператор lock (locker) { ... }
и заменить его на Interlocked.Increment(ref lockCounter)
.
Это решило мою проблему, потому что тупика избегали.
void SynchronizedMethodExample() {
/* synchronize access to this method */
if (Interlocked.Increment(ref _lockCounter) != 1) { return; }
try {
...
}
finally {
_mandatoryCounter--;
}
}