例如,假设我正在编写一个具有以下签名的方法(C#4,因此没有异步关键字):
public Task Refresh();
它将调用一个方法(也返回一个Task
)来执行通信工作,然后运行任务延续以根据检索到的数据更新一些内部状态。 例如:
public Task Refresh()
{
Task<MyData> commsTask = datasource.LoadData();
Task handleDataTask = commsTask.ContinueWith( HandleNewData );
return ?;
}
如果我返回handleDataTask
它的完成状态会正确跟踪"刷新"操作的结果,但它没有正确报告它的启动状态。
我可以将两者包装在新Task.Factory.StartNew
中并将它们创建为子任务,但是仅仅为了链接一些任务延续而假脱机新线程似乎很浪费。
当然,有一种简洁有效的方法可以使用TPL来做到这一点吗?
通常,Task.Status
仅用于了解最终状态。无论如何,您都不能依赖任务Started
,因为该状态可能随时更改。
因此,您返回的任务在完成之前是否具有"奇怪"状态并不重要。只有三种完成的状态(Completed
、Canceled
、Faulted
)才重要。
我做了一些进一步的研究,发现了一个类似的SO问题,以及一些博客文章:
没有任务完成源的任务链?
http://blogs.msdn.com/b/pfxteam/archive/2010/11/21/10094564.aspx
http://msmvps.com/blogs/jon_skeet/archive/2011/05/20/eduasync-part-7-generated-code-from-a-simple-async-method.aspx
所以有一个半答案 - 您可以创建一个表示所有子任务的任务实例,而无需假脱机新线程并将它们作为子任务附加:只需使用 TaskCompletionSource。下面的简单示例适用于我上面的问题,没有失败或取消处理:
public Task Refresh()
{
var refreshTaskSource = new TaskCompletionSource<object>();
Task<MyData> commsTask = datasource.LoadData();
Task handleDataTask = commsTask.ContinueWith( HandleNewData );
handleDataTask.ContinueWith( t => refreshTaskSource.SetResult(null) );
return refreshTaskSource.Task;
}
但是,此方法返回的任务现在直接从TaskStatus.WaitingForActivation
转换为TaskStatus.RanToCompletion
(如果我处理了这些情况,则为错误/取消)。