如何同步运行一个延续任务?

时间:2015-11-04 01:24:43

标签: c# .net vb.net asynchronous task

我声明了一个应该异步运行Task的方法,然后,当Task完成后,运行指定的委托同步

C#示例:

private void WaitUntilLoadedAsync(Process p, Action callback, int timeout = 1500)
{
    Task.Factory.StartNew(() => ProcessUtil.WaitUntilLoaded(p, timeout)).
                 ContinueWith(() => callback.Invoke()).RunSynchronously();

}

VB.Net示例:

Private Sub WaitUntilLoadedAsync(ByVal p As Process,
                                 ByVal callback As Action,
                                 Optional ByVal timeout As Integer = 1500)

    Task.Factory.StartNew(Sub() ProcessUtil.WaitUntilLoaded(p, timeout)).
                 ContinueWith(Sub() callback.Invoke()).RunSynchronously()

End Sub

但是,RunSynchronously方法会抛出一个System.InvalidOperationException例外,告诉我,无法同步运行延续Task

然后,我该怎么做?。

注(S):

  • 我能够支持Async / Await解决方案。

  • 我会保留"微小的"我的方法的逻辑或圈复杂度,我的意思是,它只是我传递Acton的方法,没有在方法之外声明的委托或其他复杂的东西,可能导致最终用户写的不仅仅是Action。该方法本身应该自动执行所有必需的操作,以同步执行该延续任务,外部没有复杂性。

2 个答案:

答案 0 :(得分:2)

听起来像是在寻找TaskContinuationsOptions.ExecuteSynchronously

MSDN

  

指定应同步执行continuation任务。如果指定了此选项,则continuation将在导致先行任务转换为其最终状态的同一线程上运行。如果在创建continuation时前提已经完成,则continuation将在创建continuation的线程上运行。如果先行的CancellationTokenSource放在finally块中(最后在Visual Basic中),则使用此选项的延续将在finally块中运行。只有非常短暂的延续才能同步执行。

     

由于任务是同步执行的,因此无需调用Task.Wait等方法来确保调用线程等待任务完成。

要使用它,只需使用重载.ContinueWith(Action<task>, TaskContinuationOptions)

将其作为标志传递

然而,如果执行父节点的线程中止(这种情况并不常见),这将不会同步。

答案 1 :(得分:2)

因此,正如我从问题和注释中理解的那样,您希望在线程池线程(后台线程)上运行任务1,并且您希望在UI上运行任务2(这是任务1的延续)线程。

以下是如何做到这一点:

private void WaitUntilLoadedAsync(Process p, Action callback, int timeout = 1500)
{
    var thread_scheduler = TaskScheduler.FromCurrentSynchronizationContext();

    Task.Factory.StartNew(() => ProcessUtil.WaitUntilLoaded(p, timeout)).
        ContinueWith(t => callback.Invoke(), CancellationToken.None, TaskContinuationOptions.None,
            thread_scheduler);
}

此代码正在做的是它要求TPL在将在UI线程中执行任务的调度程序上运行continuation任务。

这假定从UI线程调用WaitUntilLoadedAsync。如果不是这种情况,只需将thread_schedular设为实例变量(或在某个可从此方法访问的范围内),并确保从UI线程初始化它。

请注意,控件将立即返回到WaitUntilLoadedAsync的调用者,并且在任务1完成后将在UI线程上执行回调。

如果要在UI线程上执行continuation任务,只有从UI线程调用WaitUntilLoadedAsync(否则在线程池线程上执行),然后将thread_scheduler定义为以下内容:

var thread_scheduler = SynchronizationContext.Current == null
    ? TaskScheduler.Default
    : TaskScheduler.FromCurrentSynchronizationContext();