У меня есть несколько сложное приложение WPF, которое, кажется, «зависает» или застревает в вызове ожидания при попытке использовать диспетчер для вызова вызова в потоке пользовательского интерфейса.
Общий процесс:
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.
Итак, есть ли что-то, что я делаю неправильно, чего мне не хватает, очевидно или нет, или есть лучший способ обратиться к потоку пользовательского интерфейса, чтобы установить это свойство (и другие)?
Редактировать: Решением было вызвать System.Windows.Threading.Dispatcher.Run () в конце делегата потока (например, там, где выполнялась работа) - Спасибо всем, кто помог.
@Matthew - вы вообще не присоединяетесь () к новой теме, не так ли? Это объяснило бы это ...
@Marc Gravell - по памяти я в какой-то момент присоединялся к теме, но я не уверен, было ли поведение таким же, когда я не использовал это. Причина присоединения заключается в том, что я хотел заблокировать остальную часть приложения, пока работа не будет завершена, но, возможно, я могу использовать альтернативу.





Вызов синхронный - вам нужен Dispatcher.BeginInvoke. Кроме того, я считаю, что ваш пример кода должен переместить «SetValue» внутрь оператора «else».
Это похоже на тупик; это обычно происходит, если поток, вызывающий .Invoke, уже имеет блокировку / мьютекс / и т. д., которые поток пользовательского интерфейса должен завершить свою работу. Самый простой подход - использовать вместо этого BeginInvoke: в этом случае текущий поток может продолжать работать и (предположительно) вскоре снимет блокировку, позволяя пользовательскому интерфейсу получить ее. В качестве альтернативы, если вы можете определить блокировку, которая нарушает правила, вы можете намеренно снять ее на какое-то время.
Спасибо, Марк, это хорошее объяснение, однако я все еще не понимаю, почему вообще существует блокировка. Как вы сами и Пол BeginInvoke предположили, это был вариант, но не оптимальный, поэтому нет гарантии, что он завершится. Сумасшедшие странные ошибки ....
Вы говорите, что создаете новый поток STA, работает ли диспетчер в этом новом потоке?
Я получаю от "this.Dispatcher.Thread! = Thread.CurrentThread", что вы ожидаете, что это будет другой диспетчер. Убедитесь, что он запущен, иначе он не будет обрабатывать свою очередь.
Кейт, это хорошее замечание. Я недостаточно знаком с диспетчером, но разве диспетчер окна уже не запущен? Поток STA используется для создания нового окна, однако, если мне нужно запустить диспетчер вручную, он объяснит, почему он не обрабатывается ...
Если вы создаете STA самостоятельно, попробуйте вызвать Dispatcher.Run () после того, как вы покажете свое окно. Насколько я понимаю, диспетчер - это насос сообщений, и при создании нового потока пользовательского интерфейса он будет создавать диспетчер по запросу, если вы управляете созданием, вам придется вызывать диспетчер Run.
Взгляните на этот пост: eprystupa.wordpress.com/2008/07/28/…
У меня аналогичная проблема, и, хотя я все еще не уверен, каков ответ, я думаю, что ваш
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 здесь, однако я обнаружил, что не получаю обновления пользовательского интерфейса, когда выполняю этот асинхронный режим. К сожалению, когда я делаю это синхронно, я получаю состояние тупика ...
Я думаю, вы имеете в виду, если (! This.Dispatcher.CheckAccess ())
У меня также зависание с Invoke, или, если я могу BeginInvoke, мой делегат не вызывается - похоже, все делает по книге :-(
Я знаю, что это старый поток, но вот другое решение.
Я только что исправил похожую проблему. Мой диспетчер работал нормально, так что ...
Мне пришлось показать ОКНО ОТЛАДКИ -> ПОТОК, чтобы идентифицировать все потоки, которые где угодно выполняют мой код.
Проверяя каждый из потоков, я быстро видел, какой поток вызвал тупик.
Это было несколько потоков, объединяющих оператор 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--;
}
}
Я думаю, это лучше показать с помощью кода. Рассмотрим этот сценарий:
Поток A делает это:
lock (someObject)
{
// Do one thing.
someDispatcher.Invoke(() =>
{
// Do something else.
}
}
Поток B делает это:
someDispatcher.Invoke(() =>
{
lock (someObject)
{
// Do something.
}
}
На первый взгляд все может показаться прекрасным и модным, но это не так. Это приведет к тупиковой ситуации. Диспетчеры похожи на очереди для потока, и при работе с такими взаимоблокировками важно думать о них таким образом: «Какая предыдущая отправка могла заблокировать мою очередь?». Поток A войдет ... и будет отправлен под замком. Но что, если поток B входит в момент времени, когда поток A находится в коде с пометкой «Сделать что-то одно»? Что ж...
Теперь у вас красивый тупик.
Спасибо за хорошее объяснение. Сэкономил мне часы работы. Я исправил это, не получая блокировки в вызовах диспетчера (что делает ваш поток B). Есть ли другое решение этой проблемы?
@Heribert Это зависит от кода, с которым вы работаете. Подобные взаимоблокировки очень зависят от приложения. Если вы столкнулись с ситуацией, подобной описанной выше, вы можете попытаться заблокировать вызовы вне диспетчера.
вот что я сделал :) Еще раз спасибо за сообщение и ответ
@Matthew - на самом деле ничего "неоптимального" в BeginInvoke нет; если вам абсолютно не нужно обновление Теперь, это нормально. Тем не менее, вы должны быть немного осторожны с захваченными переменными (т.е. не меняйте «значение» после вызова BeginInvoke. Вообще.)