简体   繁体   English

来自新线程的Dispatcher.Invoke正在锁定我的UI

[英]Dispatcher.Invoke from a new thread is locking my UI

i'm using wpf, there's a button on my ui. 我正在使用wpf,我的ui上有一个按钮。

when the user clicks it, i have a for loop that runs a new method, on a new thread using autoresetevent. 当用户点击它时,我有一个使用autoresetevent在新线程上运行新方法的for循环。

in that method on that new thread, i'm using a label, let's call it lblStatus. 在那个新线程的方法中,我正在使用一个标签,我们称之为lblStatus。 i want to update that label on this thread that's not on the ui. 我想在这个不在ui上的线程上更新那个标签。 using wpf, i have to use Dispatcher.Invoke. 使用wpf,我必须使用Dispatcher.Invoke。

here's a sample of my code: 这是我的代码示例:

 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();
}

the program just stops here. 该计划就在这里停止。 it doesn't change the content of the label, it returns to my ui, but blocks it. 它不会改变标签的内容,它会返回我的ui,但会阻止它。
i've tried 我试过了

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

as well, but that isn't working also. 同样,但这也不起作用。 any ideas? 有任何想法吗?

The problem is that you're making it impossible for this to execute, since you're using Invoke. 问题在于,由于您正在使用Invoke,因此无法执行此操作。

Dispatcher.Invoke will not return until the UI thread processes. 在UI线程处理之前,Dispatcher.Invoke不会返回。 However, you've blocked the UI thread by calling _waitHandle.WaitOne(); 但是,您已通过调用_waitHandle.WaitOne();阻止了UI线程_waitHandle.WaitOne(); , and don't set the wait handle until AFTER this processes. ,并且在此过程之后才设置等待句柄。 The two effectively cause a dead lock. 这两个有效地导致死锁。

If you switch this to use BeginInvoke instead, the UI will queue the element, the wait handle will set, THEN the label will update. 如果您将其切换为使用BeginInvoke,则UI将对元素进行排队,等待句柄将设置,然后标签将更新。 It will work and not block, however. 然而,它会起作用而不是阻挡。

Since the two previous posts already cover the problem in your code, just a suggestion: instead of 由于之前的两个帖子已经涵盖了您的代码中的问题,只是一个建议:而不是

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

try using 尝试使用

if (!lblStatus.CheckAccess())

It's cleaner and has the exact intent you want. 它更干净,具有您想要的确切意图。 Just read about it here . 请在这里阅读它。

Best solution I have found for .net 4.5+ is using SynchronizationContext Post 我发现.net 4.5+的最佳解决方案是使用SynchronizationContext Post

Example (Task.Run's can be as many as you want in parallel accessing UI): 示例(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++;

        }
    });

}

You probably want to use BeginInvoke instead. 您可能希望使用BeginInvoke Invoke will block the thread that called it until the UI thread has run the Action, and since you're setting the priority to Background, this could take some time. Invoke将阻止调用它的线程,直到UI线程运行Action,并且由于您将优先级设置为Background,这可能需要一些时间。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM