3

我正在使用 wpf,我的用户界面上有一个按钮。

当用户单击它时,我有一个 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();
}

程序就停在这里。它不会更改标签的内容,它会返回到我的 ui,但会阻止它。
我试过了

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

也一样,但这也不起作用。有任何想法吗?

4

4 回答 4

7

问题是您无法执行此操作,因为您使用的是 Invoke。

在 UI 线程处理之前,Dispatcher.Invoke 不会返回。但是,您已通过调用阻止了 UI 线程_waitHandle.WaitOne();,并且在此进程之后才设置等待句柄。两者有效地导致死锁。

如果您将其切换为使用 BeginInvoke,UI 将排队元素,设置等待句柄,然后标签将更新。但是,它将起作用并且不会阻塞。

于 2011-12-15T23:01:56.637 回答
3

由于前两篇文章已经涵盖了您的代码中的问题,因此只是一个建议:而不是

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

尝试使用

if (!lblStatus.CheckAccess())

它更干净,并且具有您想要的确切意图。只是在这里阅读。

于 2011-12-15T23:05:59.433 回答
0

您可能想BeginInvoke改用。Invoke将阻塞调用它的线程,直到 UI 线程运行 Action,并且由于您将优先级设置为 Background,这可能需要一些时间。

于 2011-12-15T22:58:21.967 回答
0

我为 .net 4.5+ 找到的最佳解决方案是使用 SynchronizationContext Post

示例(在并行访问 UI 时,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++;

        }
    });

}
于 2018-04-04T11:08:26.670 回答