我正确使用ContinueWith吗?

时间:2015-12-07 21:55:18

标签: c# .net-4.0 task-parallel-library

我有一个启动窗口(在WPF中)的应用程序,它允许用户登录,选择数据库,进行一些其他设置,最终(成功时)显示进度条。完成进度后,将显示一个主窗口(在WinForms中)。

为了使进度条顺利更新,我尽可能地远离UI线程。这是目前的做法:

var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
var backgroundScheduler = TaskScheduler.Default;

var connectingTask = Task.Factory.StartNew(() =>
{
    System.Diagnostics.Debug.WriteLine("connecting started");

    // some code that connects to a server; may fail

    System.Diagnostics.Debug.WriteLine("connecting finishing");
}

var connectFailAction = new Action<Task>((t) =>
{
    System.Diagnostics.Debug.WriteLine("connectFail started");

    // let user know that connecting failed, then exit

    System.Diagnostics.Debug.WriteLine("connectFail finishing");
});

var postConnectAction = new Action<Task>((t) =>
{
    System.Diagnostics.Debug.WriteLine("postConnect started");

    // some initial post-connection setup code; runs in UI thread

    System.Diagnostics.Debug.WriteLine("postConnect finishing");
});

var dbLoadingAction = new Action<Task>((t) =>
{
    System.Diagnostics.Debug.WriteLine("dbLoading started");

    // code that loads settings from the database; runs in background

    System.Diagnostics.Debug.WriteLine("dbLoading finishing");
});

var runGuiAction = new Action<Task>((t) =>
{
    System.Diagnostics.Debug.WriteLine("runGui started");

    // set up the main form, show it, hide this startup progress window

    System.Diagnostics.Debug.WriteLine("runGui finishing");
});

connectingTask.ContinueWith(connectFailAction,
                            System.Threading.CancellationToken.None,
                            TaskContinuationOptions.OnlyOnFaulted,
                            uiScheduler);

connectingTask.ContinueWith(postConnectAction,
                            System.Threading.CancellationToken.None,
                            TaskContinuationOptions.OnlyOnRanToCompletion,
                            uiScheduler)
              .ContinueWith(dbLoadingAction,
                            System.Threading.CancellationToken.None,
                            TaskContinuationOptions.None,
                            backgroundScheduler)
              .ContinueWith(runGuiAction,
                            System.Threading.CancellationToken.None,
                            TaskContinuationOptions.None,
                            uiScheduler);

如果连接失败,任务将直接转到connectFailAction

否则,在UI线程上,接下来会运行postConnectAction,同时在后台运行dbLoadingAction。然后,runGuiAction运行。

这大部分工作正常,但是一个奇怪的边缘情况是CefSharp。具体来说,UI的非CefSharp部分被卡住了。 - 我无法调整表单大小,我无法与任何控件等进行互动。

由于等效代码适用于WinForms WebBrowser控件,我猜测它部分是CefSharp的问题,但部分原因可能是我对TPL的错误使用/理解。

如果我按如下方式更改代码,UI会在加载过程中卡住(不出所料),但CefSharp会正确进入。

connectingTask.ContinueWith(connectFailAction,
                            System.Threading.CancellationToken.None,
                            TaskContinuationOptions.OnlyOnFaulted,
                            uiScheduler);

connectingTask.Wait();
postConnectAction.Invoke(null);
dbLoadingAction.Invoke(null);
runGuiAction.Invoke(null);

如果(并且仅当)使用CefSharp,主线程卡在System.Windows.Threading.DispatcherSynchronizationContext.Wait中,所以我猜测上述任务之一从未干净地完成,导致事件队列无法正常刷新?< / p>

1 个答案:

答案 0 :(得分:1)

  

主线程卡在System.Windows.Threading.DispatcherSynchronizationContext.Wait中,所以我猜测上述任务之一从未干净地完成,导致事件队列无法正常刷新?

问题是后一个示例中的代码死锁。您正在调用稍后尝试在UI线程上运行延续的Task,该线程当前被connectingTask.Wait();阻止。这就是you shouldn't block on async code.

的原因

关于CefSharp,也许连接后在UI线程上运行的延续是CPU密集型的,并导致UI消息循环停止超过一段合理的时间。由于您未提供相关代码,因此无法确定。