合并不同类型的 .NET 4.0 任务/延续
Posted
技术标签:
【中文标题】合并不同类型的 .NET 4.0 任务/延续【英文标题】:Merging .NET 4.0 Tasks/continuations of different types 【发布时间】:2012-05-03 02:40:05 【问题描述】:我目前正在实现一个System.Web.Http.IActionFilter
,它调用一个内部服务来确定当前请求是否可以继续。我遇到的问题是根据Task<T2>
封装的一段逻辑返回Task<T1>
。
一个例子可能会有所帮助。
内部服务 API 是使用 Tasks 实现的。使用 .NET 4.5 的 async/await 的逻辑很简单:
public async Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
UserAuthenticationResult authResult = await HitInternalServiceAsync();
if (!authResult.IsAuthenticated)
throw new HttpResponseException("User is not authenticated", HttpStatusCode.Unauthorized);
return await continuation();
但是在 .NET 4.0 中使用旧的 Task API 会更加困难;
public Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
return HitInternalServiceAsync()
.ContinueWith(t1 =>
UserAuthenticationResult authResult = t1.Result;
if (!authResult.IsAuthenticated)
throw new HttpResponseException("User is not authenticated", HttpStatusCode.Unauthorized);
//Hack hack - this blocks the thread until the task retuned by continuation() completes
return continuation().Result;
);
当身份验证检查成功时,困难的部分就来了——然后我想等待延续函数返回的任务。
使用 .NET 4.0 时,我似乎在等待 continuation()
任务完成时明确阻止,而不是在 我的任务时指示 Tasks API 自动继续 continuation()
任务完成了。
问题:这是在 .NET 4.0 中实现此行为的唯一方法吗?
给定一个足够复杂的内部服务 API,我可以很容易地看到等待其他任务的任务数量迅速增加。
编辑: 看起来上面的 4.0 代码也不可行 - 因为延续 lambda 不会在 HttpContext.Current 等 ASP.NET 线程上下文服务中执行不可用。更好的实现是......
public Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
Task<UserAuthenticationResult> authResultTask = HitInternalServiceAsync();
var authResult = authResultTask.Result;
if (!authResult.IsAuthenticated)
throw new HttpResponseException("User is not authenticated", HttpStatusCode.Unauthorized);
return continuation();
【问题讨论】:
【参考方案1】:您的问题是,如果您不使用Result
,ContinueWith()
将返回Task<Task<HttpResponseMessage>>
,而不是您需要的Task<HttpResponseMessage>
。
幸运的是,已经有一种方法可以将任何Task<Task<T>>
转换为Task<T>
:Unwrap()
。所以只需 return continuation();
来自 ContinueWith()
lambda,然后在结果上调用 Unwrap()
。
如果您希望继续在 ASP.NET 上下文中执行,可以使用TaskScheduler.FromCurrentSynchronizationContext()
。
【讨论】:
正是我正在寻找的解决方案 - 我觉得这是一个如此明显的用例,API 似乎并不真正支持它似乎很奇怪。幸好我错了!【参考方案2】:问题:这是在 .NET 4.0 中实现此行为的唯一方法吗?
async
/await
是 C# 5.0 功能,而不是 .NET 4.5 功能。它确实使用了 .NET 4.5 中引入的某些类型,但没有其他原因需要新的运行时。
如果您使用的是 VS2010 (C# 4.0),svick 的答案是最好的。
但是,如果您使用的是 VS11 Beta (C# 5.0),还有另一种选择:您可以使用 async targeting pack 编写在 .NET 4.0 上运行的 async
/await
代码。目标包具有 .NET 4.0 的这些新类型。
【讨论】:
你是对的,我的意思是 C# 5.0 针对 .NET 4.5 作为第一个示例。关于async targeting pack
的提醒也值得赞赏 - 我们想使用 async\await 但目前要求我们针对 4.0 框架。【参考方案3】:
而不是 continuation().Result 使用 continuation().Wait()
task.wait 是阻止任务的适当方式。
根据 MSDN 文档, Task.Wait 方法:等待Task完成执行。
http://msdn.microsoft.com/en-us/library/dd235635.aspx
以下似乎相关的问题,即答案 Do the new C# 5.0 'async' and 'await' keywords use multiple cores?
【讨论】:
快速浏览 IL 显示.Result
在内部调用 .Wait()
Wait()
和 Result
在阻塞和等待方面的行为完全相同,它们都不比另一个更合适。
Wait() 如果你想让它看起来像这样,在某些情况下它肯定看起来更明确以上是关于合并不同类型的 .NET 4.0 任务/延续的主要内容,如果未能解决你的问题,请参考以下文章
.Net 4.0 无法将“System.Collections.Generic.List`1[ClassName]”类型的对象转换为“ClassName”类型
.NET 4.0 中的新“动态”变量类型是不是解决了 CLR 中的单/多方法分派问题?