2012-06-14 32 views
0

我有一个很长的运行过程,通过Nancy Module Get方法调用。我不希望浏览器在进程运行时挂起,而是希望此调用是异步的,换句话说,我希望Get方法立即返回,并让长时间运行的进程完成它的工作。然后我可以定期检查过程的状态并采取相应的行动。如何使用C#5 async/await从asp.net托管模块调用异步进程?

我是C#5的异步/等待功能的新手,但我确信这些是为了这种任务而设计的。下面的代码显示了我的尝试。我已经放入了一些记录,表明它不像预期的那样异步运行。长时间运行的进程阻止Get方法,因此浏览器挂起。

模块

public class TestModule : NancyModule 
{    
    public TestModule(ITestService testService) 
    { 
     Get["/longprocess"] = _ => 
      { 
       Log.Write("Module : Start"); 
       testService.LongProcess(); 
       Log.Write("Module : Finish"); 
       return HttpStatusCode.OK; 
      }; 
    } 
} 

服务

public interface ITestService 
{ 
    Task<bool> LongProcess(); 
} 

public class TestService : ITestService 
{ 
    public async Task<bool> LongProcess() 
    { 
     await LongProcessAsynch(); 
     return true; 
    } 

    private Task<bool> LongProcessAsynch() 
    { 
     Log.Write("LongProcess : Start"); 
     Thread.Sleep(5000); 
     //Task.Delay(5000); <- Has same effect 
     Log.Write("LongProcess : Finish"); 
     return true.AsTask(); 
    } 
} 

扩展方法

public static Task<T> AsTask<T>(this T candidate) 
{ 
    var source = new TaskCompletionSource<T>(); 
    source.SetResult(candidate); 
    return source.Task; 
} 

日志输出

14/06/2012 19:22:29 : Module : Start 
14/06/2012 19:22:29 : LongProcess : Start 
14/06/2012 19:22:34 : LongProcess : Finish 
14/06/2012 19:22:34 : Module : Finish 

您可以从日志输出的LongProcess()看到上面挡住了获取模块的回报。如果任务异步运行我希望在日志中是这个样子:

预计登录

Module : Start 
LongProcess : Start 
Module : Finish 
LongProcess : Finish 

我认为什么是真正需要的是把await在NancyModule获取方法。像下面也许是代码的东西,但我不能这样做,因为我不能标记模块构造为asnyc,所以我不能Get方法中使用await(或所以我现在相信)

Get["/longprocess"] = _ => 
    { 
     Log.Write("Module : Start"); 
     await testService.LongProcess(); 
     Log.Write("Module : Finish"); 
     return HttpStatusCode.OK; 
    }; 

感谢您的帮助,示例或指向资源的指针。

编辑1

更多的研究表明,asp.net似乎积极预防async调用默认。与Session和我认为处理非UI线程的方式有关。所以我添加了以下到我的web.config(见下文)

  • UseTaskFriendlySynchronizationContext
  • ENABLESESSIONSTATE = “假”

不幸的是,没有任何区别,我的async/await调用LongProcess()仍然阻塞。

<configuration> 
    <appSettings> 
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" /> 
    <add key="webPages:Enabled" value="false" /> 
    </appSettings> 
    <system.web> 
    <httpRuntime targetFramework="4.5"/> 
    <pages controlRenderingCompatibilityVersion="4.0" enableSessionState="false" /> 
    ... more config 
</configuration> 
+0

我不知道NancyFX是什么,但它需要支持任务的返回值,为您使用它异步。 – usr

+0

我嵌套异步调用尝试解决此问题。对'testService.LongProcess()'的初始调用是同步的,但是这会使用'await'关键字调用'LongProcessAsynch()'方法,所以这应该是异步的。你认为这是正确的吗? – biofractal

回答

1

我是新来的异步/等待的C#5的功能,但我觉得这肯定意味着只是这样的任务。

async/await旨在使异步编程容易单个进程的范围内。它们不会改变HTTP通信的性质。

当您在ASP.NET服务器上使用await(在HTTP请求的上下文中)时,您将ASP.NET线程返回到线程池。但是,ASP.NET知道HTTP请求尚未完成,因此它不会完成对客户端(浏览器)的响应。

这是设计。

您需要使用AJAX,SignalR或其他解决方案才能做到您想要的功能。

+0

我目前使用jQuery $ .ajax()使我的api异步调用。当然这不是真正的异步,因为asp.net会阻止你描述的响应。所以我唯一的选择是在一个新的线程上分离并返回一个ID?这肯定会工作,但我想仔细检查一下,我不能使用配置属性'UseTaskFriendlySynchronizationContext'的新异步功能?抱歉重新提出我的问题,但我希望在我离开之前做一个老派的线程疼痛解决方案。 – biofractal

+0

你不想剥离一个新的线程(这在ASP.NET中几乎总是出错;考虑回收)。退后一步,检查您的要求。你想发出一个命令来启动一个工作流程。 Web服务器不是为此设计的;你应该以某种方式坚持它(例如,MSMQ/Azure队列)或将它传递给另一台服务器(例如,Win32服务)。 –

+0

好的,我接受我在这里咆哮错误的树,所以我会仔细看看我正在尝试实现的目标,看看是否可以用更简单的方法完成。谢谢你的建议。 – biofractal

0

尝试这样做,它沃尔德这个样子 <%@ Page Language="C#" AutoEventWireup="true" CodeFile="XXX.aspx.cs" Inherits="xxx" Async="true" %>

中TCODE behile文件可以使任务和类型异步方法,并从中calll await任务后设置页面属性Async="true"在 aspx页面...

它可能工作