2011-05-09 4 views
2

네트워크 리소스에서 디스크로 파일을 다운로드하는 코드를 작성하고 있습니다. 네트워크에서 읽기는 비동기 적으로 수행되며 쓰기도 수행됩니다. 비동기 적 호출이 사실 동 기적으로 호출되는 문제를 관찰하고 있으므로 각 새 반복이 스택에 새로운 함수 호출을 생성합니다. 여기HttpWebRequest 비동기 호출이 스택을 채우고 Windows Phone에서 Reactive Extensions를 제거했습니다.

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); 

그러나 여기에 문제의

는 반응 라이브러리의 윈도우 폰 버전은 두 가지를 지원하기 위해 아래로 분리 사실이다 매개 변수 및 반환 매개 변수 : 내가 위에서 원하는대로 그 세 개의 매개 변수를 필요로하기 때문에

Observable.FromAsyncPattern<T1, T2, TResult> 

그래서 내가 읽기 기능을 정의 할 수 없습니다. 라이브러리의 다운로드 가능한 버전조차도 더 많은 매개 변수를 제공하지 않습니다.

마지막으로, 내 질문은 :

  1. 비동기 호출이 동 기적으로 호출되는 원래 문제를 해결 작업 스택을 채우는 다른 방법으로는 반응성 확장을 사용하는 것보다 다른 있나요?

  2. 그렇지 않으면 Windows Phone에서 사용할 수있는 Reactive Extensions의 제한된 버전을 사용하여 네트워크 스트림에서 읽고 스트림을 비동기 적으로 쓸 수 있습니까?

대단히 감사합니다!

답변

4

정말로 간단합니다 ... IAsyncResult.CompletedSynchronously이 맞는지 확인해야합니다. 그렇다면 조치를 취하여 호출 스택을 분할하십시오. 아마도 ThreadPool.QueueUserWorkItem이나 WP7과 동등 할 수 있습니까?

+0

원래 문제가 해결되었습니다. 감사. BTW는 비동기 적으로 완료되도록 완료하는 API가 있습니까? – Amiramix

+0

BTW2, 코드가 동기식으로 완료되면 두 스트림을 동시에 읽고 쓰는 것과는 차이가 있습니까? ResponseCallback 함수는 분리 된 스레드에서 이미 호출되어야하므로 동기식으로 UI에 아무런 차이가 없어야합니다. – Amiramix

+0

매번 동기화가된다면 sync API를 사용하지 않으시겠습니까? – spender

1

Here은 (는) 당신에게 적합한 예제입니다. 그리고 Windows Phone 7에는 실제로 두 개의 매개 변수와 결과 만 허용하는 Rx의 제한된 버전이 있습니다 (실제 버전에서는 29 개의 과부하 및 14 개의 가능한 매개 변수를 사용할 수 있음).

+0

데니스, 고맙지 만 내 예제에서 이러한 코드를 이미 사용하고 있습니다 (button4_Click 메서드가 끝나면 Observable.FromAsyncPattern 과 어떻게 연결되는지 확인하십시오). 내 질문에 문제가 연결을 설정하지 않고 비동기 적으로 네트워크 스트림을 읽는 것입니다. – Amiramix

관련 문제