运行由ASP.NET网页请求触发的asynchronous操作

我有一个asynchronous操作,由于各种原因需要使用HTTP调用一个ASP.NET网页触发。 当我的页面被请求时,它应该开始这个操作并立即返回一个确认给客户端。

此方法也通过WCF Web服务公开,并且完美工作。

在我第一次尝试时,抛出了一个例外,告诉我:

 在这种情况下不允许asynchronous操作。
启动asynchronous操作的页面必须具有Async
属性设置为true,asynchronous操作只能是
在PreRenderComplete事件之前的页面上启动。 

所以当然我在@Page指令中添加了Async="true"参数。 现在,我没有得到一个错误,但是页面阻塞,直到asynchronous操作完成。

我怎样才能得到一个真正的“即燃即用”页面?

编辑:一些代码的更多信息。 这比这更复杂一些,但我试图在那里得到一个总的想法。

 public partial class SendMessagePage : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { string message = Request.QueryString["Message"]; string clientId = Request.QueryString["ClientId"]; AsyncMessageSender sender = new AsyncMessageSender(clientId, message); sender.Start(); Response.Write("Success"); } } 

AsyncMessageSender类:

 public class AsyncMessageSender { private BackgroundWorker backgroundWorker; private string client; private string msg; public AsyncMessageSender(string clientId, string message) { this.client = clientId; this.msg = message; // setup background thread to listen backgroundThread = new BackgroundWorker(); backgroundThread.WorkerSupportsCancellation = true; backgroundThread.DoWork += new DoWorkEventHandler(backgroundThread_DoWork); } public void Start() { backgroundThread.RunWorkerAsync(); } ... // after that it's pretty predictable } 

如果你不关心向用户返回任何东西,你可以直接启动一个单独的线程,或者为了快速和肮脏的方法,使用一个委托并且调用它。 如果您不关心在asynchronous任务完成时通知用户,则可以忽略该callback。 尝试在SomeVeryLongAction()方法的结尾处​​放置一个断点,并在页面已经完成之后,您会看到它已经完成运行:

 private delegate void DoStuff(); //delegate for the action protected void Page_Load(object sender, EventArgs e) { } protected void Button1_Click(object sender, EventArgs e) { //create the delegate DoStuff myAction = new DoStuff(SomeVeryLongAction); //invoke it asynchrnously, control passes to next statement myAction.BeginInvoke(null, null); Button1.Text = DateTime.Now.ToString(); } private void SomeVeryLongAction() { for (int i = 0; i < 100; i++) { //simulation of some VERY long job System.Threading.Thread.Sleep(100); } } 

好的,问题在于:Async属性是为了让你的页面调用一些长时间运行的任务,这个任务也会阻塞线程,然后你的页面需要这个任务的输出,以便把信息返回给用户。 例如,如果您的页面需要调用Web服务,请等待其响应,然后使用响应中的数据来呈现您的页面。

你使用Async属性的原因是为了避免阻塞线程。 这很重要,因为ASP.NET应用程序使用线程池来提供请求,并且只有相对less量的可用线程。 而且,如果每个调用在等待Web服务调用时绑定线程,那么很快就会触及足够多的并发用户,用户将不得不等待这些Web服务调用完成。 Async属性让线程返回到线程池并为网站的其他并发访问者提供服务,而不是强迫它在等待Web服务调用返回时不做任何事情。

结果是:Async属性是为了在asynchronous任务完成之前不能渲染页面的情况而devise的,这就是为什么它不能立即渲染页面的原因。

你需要启动你自己的线程,并使其成为一个守护线程。 我不记得确切的语法,但是通过在BCL文档中search“守护进程”,您可以很容易地在文档中find它。 这意味着线程将保持您的应用程序在活动时不被closures,这非常重要,因为ASP.NET和IIS在他们认为有必要时保留“回收您的进程”的权利,并且在线程正在运行的情况下,你的任务将被停止。 制作线程守护进程将防止这种情况发生(除了一些可能的罕见边缘情况…当你在这里find文档时你会发现更多)。

守护线程是您将启动这些任务的地方。 当你告诉守护线程完成任务后,你可以立即渲染你的页面,这样页面的渲染就会立即生效。

然而,甚至比ASP.NET进程中的守护程序线程更好,也是为了执行任务而实现Windows服务。 让ASP.NET应用程序将要执行的任务传达给服务。 不需要守护进程线程,也不需要担心被回收的ASP.NET进程。 你如何告诉服务部门来完成这项任务? 也许通过WCF,或者通过插入一个logging到服务轮询的数据库表中。 或者其他一些方法。

编辑:这是另一个想法,我以前用于这个非常相同的目的。 将有关您的任务的信息写入MSMQ队列。 有另一个进程(甚至可能在另一台机器上)从这个队列中拉出来,并做耗时的任务。 插入到队列中的工作被优化为尽可能快地返回,所以当你放入队列中的数据通过线或类似的东西发送时,你的线程不会被阻塞。 这是最快的方法之一,要记下一个任务需要完成而不必等待任务执行的事实。

如果您在发出请求的.aspx页面中运行webforms,请设置Ansync =“true”。
<%@ Page Language="C#" Async="true" ... %>

您可以很容易地解决这个限制,甚至不需要将Async设置为true。

 public void Start() { new Task(() => { backgroundThread.RunWorkerAsync(); }).Start(); } 

如果在asynchronous调用Web服务时出现此错误,请确保按exception消息的指示添加Async ='true'属性?

页面顶部<Page Language ='VB'Async ='true'AutoEventWireup ='false'CodeFile ='mynewpage.aspx.vb'Inherits ='mynewpage'%>