从新线程调用Dispatcher.Invoke会锁定我的UI

3
我正在使用WPF,我的UI界面上有一个按钮。
当用户单击它时,我有一个for循环,会在一个新线程上运行一个新方法,使用AutoResetEvent。
在这个新线程上的方法中,我使用了一个标签,我们称之为lblStatus。我想在不在UI上的这个线程上更新这个标签。使用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...");

此外,但这也不起作用。有什么想法?

1
你有没有想过使用数据绑定? - GETah
如果您想在执行任何操作之前等待它完成,为什么要启动新线程? - Bhupendra
4个回答

8
问题在于您使用了Invoke,这使得执行变得不可能。
Dispatcher.Invoke不会返回,直到UI线程处理完毕。但是,您通过调用_waitHandle.WaitOne()阻塞了UI线程,并且在此处理之后才设置等待句柄。这两者实际上导致了死锁。
如果您将其切换为使用BeginInvoke,则UI将排队元素,等待句柄将设置,然后标签将更新。它将工作而不会阻止。

3
由于之前两篇文章已经涵盖了您代码中的问题,我有一个建议:不要使用

标签,而是使用CSS样式来控制段落之间的距离。这样可以提高代码的可读性和可维护性。
if (lblStatus.Dispatcher.Thread == Thread.CurrentThread)

尝试使用。
if (!lblStatus.CheckAccess())

它更加简洁,并具有您想要的确切意图。只需在这里阅读相关信息。


0

你可能想要使用BeginInvoke。相比之下,Invoke会阻塞调用它的线程,直到UI线程运行完Action,而由于你将优先级设置为Background,这可能需要一些时间。


0

我发现针对 .net 4.5+ 的最佳解决方案是使用 SynchronizationContext Post。

示例(Task.Run 可以并行访问 UI,数量可以任意):

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++;

        }
    });

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接