2017-02-20 12 views
0

나는 Download 방법을 가지고 ProgressChangedEventArgsProgress 속성을 가지고 있습니다. Download 메서드는 UI를 응답 성있게 유지하려면 비동기 적이어야합니다. 그래서 아래 코드를 준비했는데 Form.cse.Progress을 호출하면 크로스 스레드 연산 예외가 발생합니다. asyncawait을 잘못 구현하고 있습니까? EventArgs를 호출하지 않고 Form.cs에서 비동기 메서드 내에서 발생시킨 EventArgs를 사용하는 방법?

void Form1_Load(object sender, EventArgs e) 
    { 
     DownloadManager dm = new DownloadManager("http://download.thinkbroadband.com/20MB.zip", ""); 

     long size = 0; 
     bool res = false; 
     dm.checkUrl(ref size, ref res); 
     dm.ProgressChanged += dm_ProgressChanged; 
     dm.DownloadProcedure(size); 
    } 

    void dm_ProgressChanged(object sender, ProgressChangedEventArgs e) 
    { 
     Text = e.Progress.ToString("0.00"); 
    } 

Form.cs

에서
public async void DownloadProcedure(long contentLength) 
    { 
     await Task.Run(() => 
     { 
      long totalBytesReceived = 0; 
      int bytesRead = 0; 
      byte[] buffer = new byte[10 * 1024]; 
      HttpWebRequest req = url.CreateHttpWebRequest(); 
      HttpWebResponse resp = req.GetHttpWebResponse(); 
      Stream remoteStream = resp.GetResponseStream(); 

      while ((bytesRead = remoteStream.Read(buffer, 0, buffer.Length)) > 0) 
      { 
       totalBytesReceived += bytesRead; 
       double newProgress = (totalBytesReceived * 100d/contentLength); 
       if (progress != newProgress && ProgressChanged != null) 
       { 
        ProgressChanged(this, new ProgressChangedEventArgs(progress)); 
       } 
      } 
     }); 
    } 

DownloadManager.cs

에서

는이 문제를 어떻게 해결할 수 있습니까?

+3

가장 좋은 솔루션은 1이다)을 사용하여'IProgress '와'진행 ', 2)를 사용하여 비동기 API를 대신 HttpWebRequest 오히려 구식의 HttpClient를 사용할 수 있습니다 'Task.Run' 대신에. –

답변

3

먼저 이벤트 처리기를 제외한 async void을 사용하지 않아야합니다.

두 번째로 진행률 업데이트는 을 통해 이루어져야합니다. 이렇게하면 스레드 전환을 수행하는 진행 업데이트 소비자 Progress<T>을 사용할 수 있습니다.

세 번째로 Task.Run 대신 비동기 API를 사용하십시오.

private static readonly HttpClient client = new HttpClient(); 
public async Task DownloadProcedureAsync(long contentLength, IProgress<double> progress) 
{ 
    long totalBytesReceived = 0; 
    int bytesRead = 0; 
    byte[] buffer = new byte[10 * 1024]; 

    using (var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead)) 
    using (var remoteStream= await response.Content.ReadAsStreamAsync()) 
    { 
    while ((bytesRead = await remoteStream.ReadAsync(buffer, 0, buffer.Length)) > 0) 
    { 
     totalBytesReceived += bytesRead; 
     double newProgress = (totalBytesReceived * 100d/contentLength); 
     progress?.Report(newProgress); 
    } 
    } 
} 

사용법 :

async void Form1_Load(object sender, EventArgs e) 
{ 
    DownloadManager dm = new DownloadManager("http://download.thinkbroadband.com/20MB.zip", ""); 

    long size = 0; 
    bool res = false; 
    dm.checkUrl(ref size, ref res); 
    var progress = new Progress<double>(p => { Text = e.ToString("0.00"); }); 
    await dm.DownloadProcedureAsync(size, progress); 
}