合并不同类型的 .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】:

您的问题是,如果您不使用ResultContinueWith() 将返回Task&lt;Task&lt;HttpResponseMessage&gt;&gt;,而不是您需要的Task&lt;HttpResponseMessage&gt;

幸运的是,已经有一种方法可以将任何Task&lt;Task&lt;T&gt;&gt; 转换为Task&lt;T&gt;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 中合并两个数组

.Net 4.0 无法将“System.Collections.Generic.List`1[ClassName]”类型的对象转换为“ClassName”类型

.NET 4.0 中的新“动态”变量类型是不是解决了 CLR 中的单/多方法分派问题?

.Net 4.0 中都有哪些实现 IQueryable<T> 的可实例化类型?

java不同数值类型之间除法问题

什么是 iOS 4.0 位置 多任务处理能力