Я пишу некоторый код, который загружает файл с сетевого ресурса на диск.Чтение из сети выполняется асинхронно, а также запись.Я наблюдаю проблему, заключающуюся в том, что асинхронные вызовы фактически вызываются синхронно, так что каждая новая итерация создает новый вызов функции в стеке.Вот код:
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);
}
А вот фрагмент (слегка отредактированного) стека:
(...)
MainPage.ReadCallback(System.IAsyncResult asyncResult) Line 190 + 0x1b bytes C#
Stream.BeginRead(byte[] buffer, int offset, int count, System.AsyncCallback callback, object state) + 0x6c bytes
MainPage.WriteCallback(System.IAsyncResult asyncResult) Line 200 + 0x1f bytes C#
Stream.BeginWrite(byte[] buffer, int offset, int count, System.AsyncCallback callback, object state) + 0x64 bytes
FileStream.BeginWrite(byte[] array, int offset, int numBytes, System.AsyncCallback userCallback, object stateObject) + 0x7c bytes
IsolatedStorageFileStream.BeginWrite(byte[] buffer, int offset, int numBytes, System.AsyncCallback userCallback, object stateObject) + 0x1a bytes
MainPage.ReadCallback(System.IAsyncResult asyncResult) Line 190 + 0x1b bytes C#
Stream.BeginRead(byte[] buffer, int offset, int count, System.AsyncCallback callback, object state) + 0x6c bytes
MainPage.WriteCallback(System.IAsyncResult asyncResult) Line 200 + 0x1f bytes C#
Stream.BeginWrite(byte[] buffer, int offset, int count, System.AsyncCallback callback, object state) + 0x64 bytes
FileStream.BeginWrite(byte[] array, int offset, int numBytes, System.AsyncCallback userCallback, object stateObject) + 0x7c bytes
IsolatedStorageFileStream.BeginWrite(byte[] buffer, int offset, int numBytes, System.AsyncCallback userCallback, object stateObject) + 0x1a bytes
MainPage.ReadCallback(System.IAsyncResult asyncResult) Line 190 + 0x1b bytes C#
Stream.BeginRead(byte[] buffer, int offset, int count, System.AsyncCallback callback, object state) + 0x6c bytes
MainPage.WriteCallback(System.IAsyncResult asyncResult) Line 200 + 0x1f bytes C#
Stream.BeginWrite(byte[] buffer, int offset, int count, System.AsyncCallback callback, object state) + 0x64 bytes
FileStream.BeginWrite(byte[] array, int offset, int numBytes, System.AsyncCallback userCallback, object stateObject) + 0x7c bytes
IsolatedStorageFileStream.BeginWrite(byte[] buffer, int offset, int numBytes, System.AsyncCallback userCallback, object stateObject) + 0x1a bytes
MainPage.ReadCallback(System.IAsyncResult asyncResult) Line 190 + 0x1b bytes C#
Stream.BeginRead(byte[] buffer, int offset, int count, System.AsyncCallback callback, object state) + 0x6c bytes
MainPage.WriteCallback(System.IAsyncResult asyncResult) Line 200 + 0x1f bytes C#
Stream.BeginWrite(byte[] buffer, int offset, int count, System.AsyncCallback callback, object state) + 0x64 bytes
FileStream.BeginWrite(byte[] array, int offset, int numBytes, System.AsyncCallback userCallback, object stateObject) + 0x7c bytes
IsolatedStorageFileStream.BeginWrite(byte[] buffer, int offset, int numBytes, System.AsyncCallback userCallback, object stateObject) + 0x1a bytes
(...)
Этот стек показывает, что асинхронный обратный вызов вызывается немедленно, а выполнение не выполняется.не возвращайтесь к вызывающей функции, пока, вероятно, все итерации не будут закончены.Я ожидаю, что когда функция вызывает другую функцию асинхронно, обратный вызов не вызывается, пока вызывающая функция не завершится и не будет удалена из стека.Однако текущее поведение приводит к переполнению стека после нескольких сотен итераций.
Я пытаюсь обойти это ограничение (или его не следует называть ошибкой?) С помощью реактивных расширений.Поэтому я пытаюсь заменить итерацию, которая читает сетевой поток, на наблюдаемый шаблон, например:
var readerFunc = Observable.FromAsyncPattern<byte[], int, int, int>(responseStream.BeginRead, responseStream.EndRead);
Но здесь проблема в том, что версия реактивной библиотеки для Windows Phone была сокращена для поддержкитолько два параметра и возвращаемый параметр:
Observable.FromAsyncPattern<T1, T2, TResult>
Так что я не могу определить функцию чтения, как я хотел выше, потому что для этого потребуется три параметра.Даже загружаемая версия библиотеки не предоставляет больше параметров.
Наконец, мои вопросы:
Есть ли другой способ обойти исходную проблему сасинхронные вызовы, вызываемые синхронно и заполняющие стек, кроме использования Reactive Extensions?
Если нет, то как я могу читать из сетевого потока и асинхронно записывать в поток файлов, используяограниченная версия Reactive Extensions, которая доступна на Windows Phone?
Любая помощь с благодарностью!