Dispatcher.Invoke из нового потока блокирует мой пользовательский интерфейс

я использую wpf, на моем интерфейсе есть кнопка.

когда пользователь щелкает по нему, у меня есть цикл for, который запускает новый метод в новом потоке с использованием autoresetevent.

в этом методе в этом новом потоке я использую метку, назовем ее lblStatus. Я хочу обновить этот ярлык в этой теме, которого нет в пользовательском интерфейсе. используя wpf, я должен использовать Dispatcher.Invoke.

вот пример моего кода:

 Thread thread= new Thread(StartLooking);
 thread.Start();
 _waitHandle.WaitOne();

 private void StartLooking(object value)
{
 if (lblStatus.Dispatcher.Thread == Thread.CurrentThread)
        {
            lblStatus.Content = "Scanning>...";
        }
        else
        {
            lblStatus.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() => lblStatus.Content = "Scanning>>>>>")); 
        }
 _waitHandle.Set();
}

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

lblStatus.Dispatcher.Invoke(DispatcherPriority.Normal, new LblStatusThreadCheck(lblStatusThreadCheck), "Scanning...");

тоже, но это тоже не работает. Любые идеи?


person thermacore    schedule 15.12.2011    source источник
comment
Думали ли вы об использовании привязки данных?   -  person GETah    schedule 16.12.2011
comment
Зачем запускать новый поток, если вы хотите дождаться завершения операции, прежде чем что-либо можно будет сделать?   -  person is_this_the_way    schedule 16.12.2011


Ответы (4)


Проблема в том, что вы делаете невозможным выполнение этого, поскольку используете Invoke.

Dispatcher.Invoke не вернется, пока не обработается поток пользовательского интерфейса. Однако вы заблокировали поток пользовательского интерфейса, вызвав _waitHandle.WaitOne();, и не устанавливаете дескриптор ожидания до ПОСЛЕ этого процесса. Два эффективно вызывают мертвую блокировку.

Если вместо этого вы переключите это на использование BeginInvoke, пользовательский интерфейс поставит элемент в очередь, будет установлен дескриптор ожидания, ТОГДА метка будет обновлена. Однако он будет работать и не блокировать.

person Reed Copsey    schedule 15.12.2011

Поскольку два предыдущих сообщения уже охватывают проблему в вашем коде, просто предложение: вместо

if (lblStatus.Dispatcher.Thread == Thread.CurrentThread)

попробуйте использовать

if (!lblStatus.CheckAccess())

Это чище и имеет точное намерение, которое вы хотите. Просто прочитайте об этом здесь.

person Bruno Brant    schedule 15.12.2011

Вместо этого вы, вероятно, захотите использовать BeginInvoke. Invoke заблокирует вызвавший его поток до тех пор, пока поток пользовательского интерфейса не запустит действие, а поскольку вы устанавливаете приоритет в фоновом режиме, это может занять некоторое время.

person porges    schedule 15.12.2011

Лучшее решение, которое я нашел для .net 4.5+, - это использование сообщения SynchronizationContext.

Пример (Task.Run может быть сколько угодно при параллельном доступе к пользовательскому интерфейсу):

private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    var context = SynchronizationContext.Current;
    Task.Run(() =>
    {
        var i = 0;
        while (true)
        {
            context.Post((tmp) =>
            {
                uiText.Text = $"{i}";
            }), this);

            Thread.Sleep(1000);
            i++;

        }
    });

}
person Evalds Urtans    schedule 04.04.2018