Dispatcher.Invoke из нового потока блокирует мой пользовательский интерфейс - PullRequest
3 голосов
/ 16 декабря 2011

Я использую 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...");

, но это также не работает.есть идеи?

Ответы [ 4 ]

6 голосов
/ 16 декабря 2011

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

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

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

3 голосов
/ 16 декабря 2011

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

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

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

if (!lblStatus.CheckAccess())

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

0 голосов
/ 04 апреля 2018

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

Пример (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++;

        }
    });

}
0 голосов
/ 16 декабря 2011

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

...