C# 从任务继续写入ASP.NET响应输出流
我有一个http处理程序,它应该通过写来输出一些文本。文本内容是异步检索的,因此我想在ProcessRequest方法中写入响应流,如下所示:C# 从任务继续写入ASP.NET响应输出流,c#,asp.net,asynchronous,.net-4.0,task-parallel-library,C#,Asp.net,Asynchronous,.net 4.0,Task Parallel Library,我有一个http处理程序,它应该通过写来输出一些文本。文本内容是异步检索的,因此我想在ProcessRequest方法中写入响应流,如下所示: GetContent().ContinueWith(task => { using (var stream = task.Result) { stream.WriteTo(context.Response.OutputStream); } }); 但是,我得到一个带有堆栈跟踪的NullReferenceE
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()
如何消除此错误?(使用.net 4.0)此问题可能基于“上下文切换”,当任务在其自己的线程中执行时会发生这种切换 当前的
HttpContext
仅在请求线程中可用。但是,您可以创建一个“本地引用”来访问它(但这并不能完全解决您的问题-请参见下文):
现在的问题是,当执行ContinueWith
时,您的请求很可能已经完成,因此您到客户端的流已经关闭。在此之前,您需要将内容写入流 我需要划掉我答案的以下部分。Http处理程序在4.0中不支持
async
,因为这需要在4.5=>之前不可用的基类。我不知道在使用Http处理程序时,4.0中ProcessRequest
方法中的Wait
的解决方法:
我建议使用以下类似的方法:
using(var result = await GetContent())
{
stream.WriteTo(context.Response.OutputStream);
}
以及适当的方法。
最近,我遇到了一个类似的任务,即编写一个ashx处理程序来异步放置响应。其目的是生成一些大字符串,执行I/O并将其返回到响应流中,从而为我将要开发的I/O绑定严重的应用程序将ASP.NET与其他应用程序进行基准测试。这就是我最终要做的(它是有效的):
private StringBuilder有效负载=null;
私有异步void processAsync()
{
var r=new Random(DateTime.Now.Ticks.GetHashCode());
//生成108kb的随机字符串
有效载荷=新的StringBuilder();
对于(变量i=0;i<54000;i++)
payload.Append((char)(r.Next(65,90));
//创建一个唯一的文件
var fname=“”;
做
{
//fname=@“c:\source\csharp\asyncdemo\”+r.Next(1999999999).ToString()+“.txt”;
fname=r.Next(1999999999).ToString()+“.txt”;
}while(File.Exists(fname));
//以异步方式将字符串写入磁盘
使用(FileStream fs=newfilestream(fname,FileMode.CreateNew,FileAccess.Write,FileShare.None,
bufferSize:4096,UseAync:true)
{
var bytes=(new System.Text.ascienceoding()).GetBytes(payload.ToString());
等待fs.WriteAsync(字节,0,字节.长度);
fs.Close();
}
//以异步方式从磁盘读回字符串
有效载荷=新的StringBuilder();
//文件流;
//Append(等待fs.ReadToEndAsync());
使用(var fs=newfilestream(fname,FileMode.Open,FileAccess.Read,
FileShare.Read,bufferSize:4096,useAync:true){
国际货币联盟;
字节[]缓冲区=新字节[0x1000];
while((numRead=wait fs.ReadAsync(buffer,0,buffer.Length))!=0){
Append(Encoding.Unicode.GetString(buffer,0,numRead));
}
}
//fs.Close();
//File.Delete(fname);//删除文件
}
公共void ProcessRequest(HttpContext上下文)
{
任务任务=新任务(processAsync);
task.Start();
task.Wait();
//将字符串写回响应流
context.Response.ContentType=“text/plain”;
context.Response.Write(payload.ToString());
}
您需要实现IHttpAsyncHandler
。检查
除此之外,您还可以使用async/await
复制流(注意下面的copyanc
)。要能够在VS2012+中使用async/await
和target.NET4.0,请将包添加到项目中
这样,就不会阻塞不必要的线程。完整示例(未经测试):
你能用现代风格的c#吗?您可以在
async
/wait
模式中执行此操作吗?在调用ContinueWith
时,响应可能已经发送了。O,忘了提及-我使用.net 4.0,然后将我的问题回答为“否”。我为您的问题添加了标签,表示我使用的是VS 2013No,错误发生在MemoryStream.WriteTo
中。这意味着上下文不是空的。@usr:True,那么可能是因为我的第二点:流在到达BufferData
…时已经关闭,这不会导致BCL内部崩溃。它会导致一些适当的异常……为什么NullReferenceException
不是“适当的异常”?因为它表明发生了错误,并且不能帮助调用方解决问题。BCL中的所有类都设计为不会因nullref、索引越界等情况而崩溃。。。它们在描述性消息中崩溃。这不是真正的异步,因为请求线程在任务中被阻塞。Wait()
-要“真正异步”,应该在等待期间释放该线程(这可以通过使用Wait
)来实现)。除了@chrfin的点之外,将async void
方法传递给新任务(processAsync)
无法按您可能认为的方式工作。在processAsync
@chrfin中到达第一个Wait
后,此任务即完成-原始问题还包含使用Wait()方法的相同逻辑。Wait()实际上并不阻止较低级别的请求。阅读这篇MSDN文章:和Wait()实际上并不阻止较低级别的请求
——是的。再次阅读你的链接文章!它可能不使用一个额外的线程池线程,但它肯定会阻止请求线程,这样IIS就不能用它执行另一个请求——这就是s中“异步”的要点
var outputStream = context.Response.OutputStream;
GetContent().ContinueWith(task =>
{
using (var stream = task.Result)
{
stream.WriteTo(outputStream);
}
});
using(var result = await GetContent())
{
stream.WriteTo(context.Response.OutputStream);
}
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());
}
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
}