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

How to run synchronouslly a continuation 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 不能 运行 同步。

那我该怎么办呢?

备注:

听起来您正在寻找 TaskContinuationsOptions.ExecuteSynchronously

根据 MSDN:

Specifies that the continuation task should be executed synchronously. With this option specified, the continuation runs on the same thread that causes the antecedent task to transition into its final state. If the antecedent is already complete when the continuation is created, the continuation will run on the thread that creates the continuation. If the antecedent's CancellationTokenSource is disposed in a finally block (Finally in Visual Basic), a continuation with this option will run in that finally block. Only very short-running continuations should be executed synchronously.

Because the task executes synchronously, there is no need to call a method such as Task.Wait to ensure that the calling thread waits for the task to complete.

要使用它,只需使用重载将其作为标志传递即可 .ContinueWith(Action<task>, TaskContinuationOptions)

但是,如果执行父线程的线程中止(这种情况不常见),这将不是同步的。

因此,据我从问题和评论中了解到,您想 运行 线程池线程(后台线程)上的任务 1 并且您想要 运行 任务 2(哪个是 UI 线程上任务 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 线程中执行任务。

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

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

如果仅当从 UI 线程调用 WaitUntilLoadedAsync 时才想在 UI 线程上执行延续任务(否则在线程池线程上执行) , 然后定义 thread_scheduler 如下:

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