2008-11-05 8 views
21

У меня есть несколько сложное приложение WPF, которое, кажется, «висит» или застревает в вызове Wait при попытке использовать диспетчера для вызова вызова в потоке пользовательского интерфейса.WPF Dispatcher.Invoke 'hanging'

Общий процесс:

  1. Обрабатывать событие щелчка на кнопке
  2. Создать новую тему (STA), который: создает новый экземпляр ведущего и пользовательского интерфейса, а затем вызывает метод Disconnect
  3. Disconnect затем устанавливает свойство на UI называется Имя
  4. инкубатор для имени затем использует следующий код для установки свойства:

    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. 

Моя проблема заключается в том, что когда диспетчер вызова метод называется он подвисает каждый раз, и CallStack указывает, что его во сне, ждать или присоединиться в рамках реализации Invoke.

Итак, есть ли что-то, что я делаю неправильно, что у меня отсутствует, очевидно или нет, или есть лучший способ обратиться к потоку пользовательского интерфейса, чтобы установить это свойство (и другие)?

Edit: Решение было назвать System.Windows.Threading.Dispatcher.Run() в конце нити делегата (например, когда работа выполняется) - Спасибо всем, кто помог.

+0

@Matthew - на самом деле, нет ничего «неоптимального» о BeginInvoke; если вам не нужно обновление * сейчас *, это нормально. Вы должны быть немного осторожны с захваченными переменными, хотя (т. Е. Не меняйте «значение» после вызова BeginInvoke. На всех.) –

+0

@Matthew - вы не присоединяетесь() к новой теме вообще, не так ли? Это объяснило бы это ... –

+1

@Marc Gravell - из памяти я в какой-то момент входил в поток, но я не уверен, было ли поведение таким же, когда я не использовал это. Причина подключения - я хотел заблокировать остальную часть приложения, пока работа не завершится, но, возможно, я смогу использовать альтернативу. –

ответ

5

Вы говорите, что вы создаете новый поток STA, является диспетчером в этом новом потоке?

Я получаю от «this.Dispatcher.Thread! = Thread.CurrentThread», что вы ожидаете, что это будет другой диспетчер. Убедитесь, что он работает, иначе он не будет обрабатывать свою очередь.

+0

Keith, Это хороший момент. Я не достаточно знаком с диспетчером, но разве диспетчер окон не будет запущен? Тема STA используется для создания нового окна, однако, если мне нужно запустить диспетчер вручную, это объяснит, почему он не обрабатывается ... –

+0

Если вы создаете STA, попробуйте вызвать Dispatcher.Run() после того, как вы покажете свое окно , Я понимаю, что диспетчер - это насос сообщений, и если вы создаете новый поток пользовательского интерфейса, он будет создавать диспетчер при запросе, если вы управляете созданием, вы должны вызвать «Выполнить» на диспетчере. – Keith

+1

Взгляните на это сообщение: http://eprystupa.wordpress.com/2008/07/28/running-wpf-application-with-multiple-ui-threads/ – Keith

10

Invoke является синхронным - вы хотите Dispatcher.BeginInvoke. Кроме того, я считаю, что ваш образец кода должен перемещать «SetValue» внутри оператора «else».

2

Это звучит как тупик; это обычно случается, если поток, вызывающий .Invoke, уже содержит блокировку/mutex/etc, которую должен выполнить поток пользовательского интерфейса, чтобы завершить его работу. Самый простой подход - использовать BeginInvoke вместо этого: таким образом, текущий поток может продолжать работать, и он (предположительно) освободит блокировку в ближайшее время, позволяя пользовательскому интерфейсу реализовать его. В качестве альтернативы, если вы можете идентифицировать блокировку нарушения, вы можете намеренно отпустить ее на время.

+0

Спасибо, Марк, Это объяснение хорошо, однако я все еще не понимаю, почему в первую очередь есть замок.Как было предложено самим вами, и Пол Бегин Инвуок был вариантом, но не оптимальным, нет гарантии его завершения. Сумасшедшие странные ошибки .... –

1

У меня аналогичная проблема, и пока я еще не уверен, что ответ, я думаю, что ваш

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 здесь, однако я обнаружил, что я не получаю обновления пользовательского интерфейса, когда я делаю это асинхронное. К сожалению, когда я делаю это синхронно, я получаю условие взаимоблокировки ...

3

Я думаю, вы имеете в виду, если (! This.Dispatcher.CheckAccess())

Я также адресности зависание с Invoke, или если я могу BeginInvoke мой делегат не вызывается - кажется, делает все по книге :-(

0

Я знаю, что это старая нить, но вот еще одно решения

я просто исправил аналогичную проблему Моей диспетчерская работала отлично, так что ...

Я должен был показать DEBUG -..> нИТКИ ОКНА определить все потоки, выполняются мой код в любом месте.

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

Это было несколько потоков, объединяющих инструкцию 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--; 
    } 
} 
7

Я думаю, что это лучше показано кодом. Рассмотрим такой сценарий:

Поток А делает это:

lock (someObject) 
{ 
    // Do one thing. 
    someDispatcher.Invoke(() => 
    { 
     // Do something else. 
    } 
} 

Thread B делает это:

someDispatcher.Invoke(() => 
{ 
    lock (someObject) 
    { 
     // Do something. 
    } 
} 

Все может показаться хорошо и денди, на первый взгляд, но это не так. Это приведет к тупиковой ситуации. Диспетчеры похожи на очереди для потока, и, когда они имеют дело с такими взаимоблокировками, важно подумать о них так: «Какая предыдущая отправка могла замять мою очередь?». Тема А войдет в ... и отправится под замок. Но, что, если поток B входит в момент времени, когда Thread A находится в коде, отмеченном «Сделать одну вещь»? Ну ...

  • Резьба A имеет блокировку на someObject и запускает некоторый код.
  • Thread B теперь отправляет сообщения, и диспетчер попытается получить блокировку на someObject, забив ваш диспетчер, так как Thread A уже имеет эту блокировку.
  • Резьба A затем будет стоять в очереди на другой пункт отправки. Этот элемент никогда не будет запущен, потому что ваш диспетчер никогда не завершит обработку вашего предыдущего запроса; его уже застряли.

У вас теперь есть прекрасный тупик.

+0

Спасибо за хорошее объяснение. Сэкономил мне часы работы. Я исправил это, не приобретая блокировок в диспетчерских вызовах (что делает ваш поток B). Есть ли другое решение для этой проблемы? – Heribert

+0

@Heribert Это зависит от кода, с которым вы работаете. Подобные блокировки очень специфичны для приложений. Если вы столкнулись с подобным случаем, вы можете попытаться заблокировать его за пределами вызовов диспетчера. – Alexandru

+0

вот что я сделал :) Еще раз спасибо за сообщение и ответ – Heribert

 Смежные вопросы

  • Нет связанных вопросов^_^