我声明了一个应该异步运行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
。该方法本身应该自动执行所有必需的操作,以同步执行该延续任务,外部没有复杂性。
答案 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();