[英]Dispatcher.Invoke from a new thread is locking my UI
我正在使用wpf,我的ui上有一个按钮。
当用户点击它时,我有一个使用autoresetevent在新线程上运行新方法的for循环。
在那个新线程的方法中,我正在使用一个标签,我们称之为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();
}
该计划就在这里停止。 它不会改变标签的内容,它会返回我的ui,但会阻止它。
我试过了
lblStatus.Dispatcher.Invoke(DispatcherPriority.Normal, new LblStatusThreadCheck(lblStatusThreadCheck), "Scanning...");
同样,但这也不起作用。 有任何想法吗?
问题在于,由于您正在使用Invoke,因此无法执行此操作。
在UI线程处理之前,Dispatcher.Invoke不会返回。 但是,您已通过调用_waitHandle.WaitOne();
阻止了UI线程_waitHandle.WaitOne();
,并且在此过程之后才设置等待句柄。 这两个有效地导致死锁。
如果您将其切换为使用BeginInvoke,则UI将对元素进行排队,等待句柄将设置,然后标签将更新。 然而,它会起作用而不是阻挡。
由于之前的两个帖子已经涵盖了您的代码中的问题,只是一个建议:而不是
if (lblStatus.Dispatcher.Thread == Thread.CurrentThread)
尝试使用
if (!lblStatus.CheckAccess())
它更干净,具有您想要的确切意图。 请在这里阅读它。
我发现.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++;
}
});
}
您可能希望使用BeginInvoke
。 Invoke
将阻止调用它的线程,直到UI线程运行Action,并且由于您将优先级设置为Background,这可能需要一些时间。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.