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函数,因为这需要三个参数。即使是库的可下载版本也没有提供更多参数

最后,我的问题是:

  • 除了使用反应式扩展之外,还有其他方法解决异步调用被同步调用并填满堆栈的原始问题吗

  • 如果没有,那么如何使用Windows Phone上可用的有限版本的被动扩展名从网络流读取数据并异步写入文件流


  • 非常感谢您的帮助

    非常简单,真的。。。您需要检查
    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>