C# 在Windows Phone上,HttpWebRequest异步调用填满堆栈并剥离响应式扩展
我正在编写一些代码,将文件从网络资源下载到磁盘。从网络读取和写入都是异步完成的。我观察到一个问题,异步调用实际上是同步调用的,因此每个新的迭代都会在堆栈上创建一个新的函数调用。代码如下:C# 在Windows Phone上,HttpWebRequest异步调用填满堆栈并剥离响应式扩展,c#,windows-phone-7,stack-overflow,system.reactive,C#,Windows Phone 7,Stack Overflow,System.reactive,我正在编写一些代码,将文件从网络资源下载到磁盘。从网络读取和写入都是异步完成的。我观察到一个问题,异步调用实际上是同步调用的,因此每个新的迭代都会在堆栈上创建一个新的函数调用。代码如下: private IsolatedStorageFileStream fileStream = null; private HttpWebRequest webRequest = null; private Stream responseStream = null; private long respons
private IsolatedStorageFileStream fileStream = null;
private HttpWebRequest webRequest = null;
private Stream responseStream = null;
private long responsePosition = 0;
private static int BufferSize = 4096;
private byte[] bufferRead = new byte[BufferSize];
private void button4_Click(object sender, RoutedEventArgs e)
{
string fileName = "TestFile.mp3";
using( var store = IsolatedStorageFile.GetUserStoreForApplication() )
{
if( store.FileExists(fileName) )
{
store.DeleteFile(fileName);
}
fileStream = store.OpenFile(fileName, FileMode.CreateNew, FileAccess.Write, FileShare.Read);
webRequest = WebRequest.Create(new Uri(mpsUri.Text)) as HttpWebRequest;
var observableRequest = Observable.FromAsyncPattern<WebResponse>(webRequest.BeginGetResponse, webRequest.EndGetResponse);
Observable.Timeout(observableRequest.Invoke(), TimeSpan.FromMinutes(2))
.Subscribe(response => { ResponseCallback(response); }, exception => { TimeoutCallback(); });
}
}
private void TimeoutCallback()
{
webRequest.Abort();
MessageBox.Show("Request timed-out");
}
private void ResponseCallback(WebResponse webResponse)
{
if( (webResponse as HttpWebResponse).StatusCode != HttpStatusCode.OK )
{
MessageBox.Show("Download error1");
}
else
{
responseStream = webResponse.GetResponseStream();
if( responsePosition != 0 )
{
responseStream.Position = responsePosition;
}
IAsyncResult readResult = responseStream.BeginRead(bufferRead, 0, BufferSize, new AsyncCallback(ReadCallback), null);
return;
}
webResponse.Close();
MessageBox.Show("Download error2");
}
private void ReadCallback(IAsyncResult asyncResult)
{
int bytes = responseStream.EndRead(asyncResult);
DLog.Info("store:{0}, current size:{1}", bytes, fileStream.Length);
if( bytes > 0 )
{
fileStream.BeginWrite(bufferRead, 0, bytes, WriteCallback, null);
return;
}
responseStream.Close();
MessageBox.Show("Download error3");
}
private void WriteCallback(IAsyncResult asyncResult)
{
DLog.Info("Stored!");
responseStream.BeginRead(bufferRead, 0, BufferSize, new AsyncCallback(ReadCallback), null);
}
这个堆栈显示异步回调被立即调用,并且在所有迭代可能完成之前,执行不会返回到调用函数。我希望当一个函数异步调用另一个函数时,在调用函数完成并从堆栈中删除之前不会调用回调。但是,当前的行为会导致堆栈在几百次迭代后溢出
我正试图通过使用反应式扩展来解决这个限制(或者这不应该被称为bug吗?)。因此,我试图用一种可观察的模式来代替读取网络流的迭代,例如:
var readerFunc = Observable.FromAsyncPattern<byte[], int, int, int>(responseStream.BeginRead, responseStream.EndRead);
var readerFunc=Observable.FromAsyncPattern(responseStream.BeginRead,responseStream.EndRead);
但这里的问题是,反应式库的Windows Phone版本已被剥离,仅支持两个参数和返回参数:
Observable.FromAsyncPattern<T1, T2, TResult>
Observable.FromAsyncPattern
所以我不能像上面所说的那样定义reading函数,因为这需要三个参数。即使是库的可下载版本也没有提供更多参数
最后,我的问题是:
非常感谢您的帮助 非常简单,真的。。。您需要检查
IAsyncResult.CompletedSynchronously
是否为真。如果是这样,请采取措施分解调用堆栈。也许您可以ThreadPool.QueueUserWorkItem
或WP7等效项?就是一个例子。是的,Windows Phone 7确实有一个有限版本的Rx,它只接受两个参数和一个结果(而在实际版本中,您可以使用29个重载和14个可能的参数)。这解决了最初的问题。谢谢顺便问一下,是否有API强制异步完成?顺便问一下,如果代码是同步完成的,那么与简单地同步读取和写入两个流有什么区别吗?ResponseCallback函数应该已经在一个单独的线程中调用了,所以同步调用不会对UI造成任何影响。如果您确定它每次都是同步的,那么是的,为什么不使用同步API?是的,到目前为止,我只看到它被同步调用。在更改代码后,我发现同步读/写工作非常好(不会减慢UI,因为它已经在单独的线程中运行)。当然,按照您的建议破坏堆栈也可以,但这意味着更多的代码无法真正测试(因为我无法强制异步完成调用)。有点可笑的是,他们添加了这么多管道来使用异步模式,最终解决了一个现实世界中仍然需要同步读/写的场景。无论如何,谢谢你的帮助。谢谢Dennis,但我已经在我的示例中使用了这样的代码(请参见button4\u Click方法末尾的Observable.FromAsyncPattern如何建立连接)。在我的问题中,问题在于异步读取网络流,而不是建立连接。
Observable.FromAsyncPattern<T1, T2, TResult>