Подтвердить что ты не робот

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() в конце делегата потока (например, когда работа выполнялась) - Спасибо всем, кто помог.

4b9b3361

Ответ 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--;
    }
}