Task.Wait vs Task.RunSyncronously任务调用WPF Dispatcher.Invoke

dmc*_*mck 5 .net c# wpf multithreading task-parallel-library

我有一个Task我正在开始并希望等待在WPF应用程序中完成.在这个任务中,我调用了一个Action调度员.

如果我使用Task.Wait()它似乎挂起,好像方法永远不会完成.此外,Dispatcher.Invoke中的断点永远不会被命中.

如果我使用Task.RunSyncronously()它似乎工作正常并且Dispatcher内的断点被击中.

为什么会有区别?

代码示例如下:

public void ExampleMethod()
{
    // When doing the following:
    var task = new Task(LoadStuff);

    // This never returns:
    task.Start();
    task.Wait();

    // This version, however, does:
    task.RunSyncronously();
}

private void LoadStuff()
{
    ObservableCollection<StuffObj> stuff = Stuff.Load(arg1, true);

    DispatchHelper.RunOnDispatcher(() =>
    {
        ...
    });
}

public static class DispatchHelper
{
    public static void RunOnDispatcher(Action action)
    {
        Application.Current.Dispatcher.Invoke(action);
    }
}    
Run Code Online (Sandbox Code Playgroud)

Ser*_*rvy 5

是的,有一个重大区别.如果您使用RunSyncronously,只需在UI线程中运行任务即可.如果你在后台线程和我们中启动它,Wait那么代码在后台线程中运行,并且UI线程被阻止.如果该任务中的代码正在调用UI线程,并且UI线程被阻塞(由Wait),那么您已经创建了死锁,并且应用程序将保持冻结状态.

请注意,如果您RunSyncronously在非UI线程上使用该任务,并且UI线程被其他东西阻止,您仍会看到死锁.

现在,至于你应该做什么,这里有两个选择:

  1. 任务本身实际上并不需要很长时间,它确实应该在UI线程中而不是在后台线程中运行.UI线程不会被暂停(暂时)足够长的时间,直接在UI中完成所有这些工作.如果是这种情况,你甚至可能不应该把它变成一个Task,只需将代码放在一个方法中并调用该方法即可.

  2. 该任务确实需要很长时间才能运行,然后在完成该工作后更新UI.如果是这种情况那么重要的是它不是RunSyncronously在后台线程中启动的.为了防止死锁这将意味着你需要你的整个应用程序不是通过阻止用户界面线程Wait调用.如果在任务完成后要运行某些代码,则需要执行的操作是向任务添加延续.在C#4.0中,可以通过调用ContinueWith任务并添加要运行的委托来完成.在C#5.0+中你可以改为await相关的任务(而不是Wait在它上面,这实际上是一个很大的区别)它会自动连接方法的其余部分作为你的继续运行(实际上它是一个显式ContinueWith调用的语法糖,但它是一个非常有用的).