我有一个http处理程序,应该写入输出一些文本。文本内容是异步检索的,所以我想在ProcessRequest方法中写入响应流,如下所示:
GetContent().ContinueWith(task =>
{
using (var stream = task.Result)
{
stream.WriteTo(context.Response.OutputStream);
}
});
但是,我得到一个带有堆栈跟踪的NullReferenceException
in System.Web.HttpWriter.BufferData(Byte[] data, Int32 offset, Int32 size, Boolean needToCopyData)
in System.Web.HttpWriter.WriteFromStream(Byte[] data, Int32 offset, Int32 size)
in System.Web.HttpResponseStream.Write(Byte[] buffer, Int32 offset, Int32 count)
in System.IO.MemoryStream.WriteTo(Stream stream)
in SomeHandler.<>c__DisplayClass1.<ProcessRequest>b__0(Task`1 task) in SomeHandler.cs:line 33
in System.Threading.Tasks.ContinuationTaskFromResultTask`1.InnerInvoke()
in System.Threading.Tasks.Task.Execute()
如果我在task.Wait()之后不使用ContinueWith并写入响应 - 没有错误,但显然它不是解决方案。
var task = GetContent();
task.Wait();
using (var stream = task.Result)
{
stream.WriteTo(context.Response.OutputStream);
}
如何消除此错误? (使用.net 4.0)
答案 0 :(得分:4)
您需要实施IHttpAsyncHandler
。检查"Walkthrough: Creating an Asynchronous HTTP Handler"。
最重要的是,您可以使用async/await
复制流(请注意下面的CopyAsync
)。为了能够使用async/await
并将.NET 4.0与VS2012 +作为目标,请将Microsoft.Bcl.Async
包添加到您的项目中。
这样,没有线程被不必要阻止。一个完整的例子(未经测试):
public partial class AsyncHandler : IHttpAsyncHandler
{
async Task CopyAsync(HttpContext context)
{
using (var stream = await GetContentAsync(context))
{
await stream.CopyToAsync(context.Response.OutputStream);
}
}
#region IHttpAsyncHandler
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
{
return new AsyncResult(cb, extraData, CopyAsync(context));
}
public void EndProcessRequest(IAsyncResult result)
{
// at this point, the task has compeleted
// we use Wait() only to re-throw any errors
((AsyncResult)result).Task.Wait();
}
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
throw new NotImplementedException();
}
#endregion
#region AsyncResult
class AsyncResult : IAsyncResult
{
object _state;
Task _task;
bool _completedSynchronously;
public AsyncResult(AsyncCallback callback, object state, Task task)
{
_state = state;
_task = task;
_completedSynchronously = _task.IsCompleted;
_task.ContinueWith(t => callback(this), TaskContinuationOptions.ExecuteSynchronously);
}
public Task Task
{
get { return _task; }
}
#region IAsyncResult
public object AsyncState
{
get { return _state; }
}
public System.Threading.WaitHandle AsyncWaitHandle
{
get { return ((IAsyncResult)_task).AsyncWaitHandle; }
}
public bool CompletedSynchronously
{
get { return _completedSynchronously; }
}
public bool IsCompleted
{
get { return _task.IsCompleted; }
}
#endregion
}
#endregion
}
答案 1 :(得分:0)
此问题可能基于“上下文切换”,该任务在其自己的线程中执行时发生。
当前HttpContext
仅在请求线程中可用。但是你可以制作一个“本地参考”来访问它(但这不能完全解决你的问题 - 见下文):
var outputStream = context.Response.OutputStream;
GetContent().ContinueWith(task =>
{
using (var stream = task.Result)
{
stream.WriteTo(outputStream);
}
});
现在的问题是,在执行ContinueWith
时您的请求很可能已经完成,因此您的客户端流已经关闭。
在此之前,您需要将内容写入流中。
我需要完成答案的以下部分。 Http处理程序不是async
- 在4.0中有能力,因为这会占用4.5 {>之前不可用的HttpTaskAsyncHandler
基类。使用http处理程序时,我不知道4.0中Wait
方法中ProcessRequest
的方法:
我建议使用以下内容:
using(var result = await GetContent())
{
stream.WriteTo(context.Response.OutputStream);
}
和相应的NuGet-Package for await
。
击>
答案 2 :(得分:-3)
最近,我遇到了类似的任务,要编写一个ashx处理程序来异步放置响应。目的是生成一些大字符串,执行I / O并将其返回到响应流上,从而将ASP.NET与其他languages like node.js基准测试用于严重I / O绑定的应用程序我将去开发。这就是我最终做的事情(它有效):
private StringBuilder payload = null;
private async void processAsync()
{
var r = new Random (DateTime.Now.Ticks.GetHashCode());
//generate a random string of 108kb
payload=new StringBuilder();
for (var i = 0; i < 54000; i++)
payload.Append( (char)(r.Next(65,90)));
//create a unique file
var fname = "";
do
{
//fname = @"c:\source\csharp\asyncdemo\" + r.Next (1, 99999999).ToString () + ".txt";
fname = r.Next (1, 99999999).ToString () + ".txt";
} while(File.Exists(fname));
//write the string to disk in async manner
using(FileStream fs = new FileStream(fname,FileMode.CreateNew,FileAccess.Write, FileShare.None,
bufferSize: 4096, useAsync: true))
{
var bytes=(new System.Text.ASCIIEncoding ()).GetBytes (payload.ToString());
await fs.WriteAsync (bytes,0,bytes.Length);
fs.Close ();
}
//read the string back from disk in async manner
payload = new StringBuilder ();
//FileStream ;
//payload.Append(await fs.ReadToEndAsync ());
using (var fs = new FileStream (fname, FileMode.Open, FileAccess.Read,
FileShare.Read, bufferSize: 4096, useAsync: true)) {
int numRead;
byte[] buffer = new byte[0x1000];
while ((numRead = await fs.ReadAsync (buffer, 0, buffer.Length)) != 0) {
payload.Append (Encoding.Unicode.GetString (buffer, 0, numRead));
}
}
//fs.Close ();
//File.Delete (fname); //remove the file
}
public void ProcessRequest (HttpContext context)
{
Task task = new Task(processAsync);
task.Start ();
task.Wait ();
//write the string back on the response stream
context.Response.ContentType = "text/plain";
context.Response.Write (payload.ToString());
}