WPF 调度程序线程冻结主要 window

WPF Dispatcher Thread Freeze main window

而不是在后台工作 - 此代码仍然冻结我的程序:

private void button_Click(object sender, RoutedEventArgs e)
{
    this.Dispatcher.BeginInvoke(new Action(() =>
    {
        Thread.Sleep(5000);
        label.Content = "Done";
    }), DispatcherPriority.Normal);
}

我试过 Thread/Tasks,线程示例:

private void button_Click(object sender, RoutedEventArgs e)
{
    var t = new Thread(new ThreadStart(runtask));
    t.Start();
}

private void runtask()
{
    this.Dispatcher.BeginInvoke(new Action(() =>
    {
        Thread.Sleep(5000);
        label.Content = "Done";
    }), DispatcherPriority.Normal);
}

任务示例:

private void button_Click(object sender, RoutedEventArgs e)
{
    Task.Run(() =>
    {
        Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() =>
        {
            Thread.Sleep(5000);
            label.Content = "Done";
        }));
    });
}

而且我的程序仍然冻结。有什么建议吗?

来自Dispatcherdocumentation class:

Provides services for managing the queue of work items for a thread.

来自Dispatcher.BeginInvokedocumentation

Executes the specified delegate asynchronously with the specified arguments on the thread that the Dispatcher was created on.

这里“异步”指的是副线程,不是主线程。因为主要的归主 Dispatcher 所有。这意味着在该 Dispatcher 上每次调用 InvokeBeginInvoke,无论是从哪个线程,都会将调用的 Action 放入主线程必须执行的操作队列中,但是从main Thread 它们会同步执行,一个接一个。

例如,如果您在 Dispatcher 上在 10 毫秒内放置 3 个类似 Thread.Sleep(1000); 的 Action,无论是 Invoke 还是 BeginInvoke 以及是否来自 Thread,Dispatcher 都会使 UI线程同步执行3个Action,所以他们总共需要3000毫秒。

也许关于 BeginInvoke 的文档可以写得更好,例如:

Executes the specified delegate with the specified arguments on the thread that the Dispatcher was created on. The specified delegate is executed asynchronously from the point of view of the calling thread.

现在...InvokeBeginInvoke?

使用Invoke,辅助线程对调度程序说:让我们在主线程上执行这个,在你的线程的工作完成之前不敢return完成的。到那时我才会继续.

例如,如果你这样写:

this.Dispatcher.Invoke(new Action(() =>
    {
        Thread.Sleep(5000);
        Debug.WriteLine("After Sleep");
    }));
Debug.WriteLine("Continuation on secondary Thread");

控制台将在 ~ 5000 毫秒后打印:

"After Sleep"

"Continuation on secondary Thread"

使用 BeginInvoke,相反,线程说:“嘿,调度员,在主线程上排队这个操作,但是 return 尽快这样我可以立即继续我的工作.

在这种情况下,控制台将立即打印:

"Continuation on secondary Thread"

在 ~ 5000 毫秒之后:

"After Sleep"


现在,如果您的目的是在后台执行一些繁重的操作,您应该了解 async/await 模式,可从 .NET 4.5 和 C# 5.0 获得。

在你的例子中,我会写:

private async void button_Click(object sender, RoutedEventArgs e)
{
    await Task.Delay(5000); // await a heavy operation executed in background

    label.Content = "Done"; // control back to the UI Thread that executes this
}

如果您的 UI 访问是您方法的最后一个,您可以使用这个小扩展。

https://mitsufu.wordpress.com/2015/08/03/dispatcher-switchto/

private void Button_Click(object sender, RoutedEventArgs e)
{
    Task.Run(async () =>
    {
        //heavy background operation
        await Dispatcher.SwitchTo();
        Title = "ok";
    });
}