2017-02-20 19 views
0

我有一個Download方法和ProgressChangedEventArgs攜帶Progress屬性。 Download方法必須異步以保持UI響應。所以我準備下面的代碼,但在Form.cs調用e.Progress時會拋出交叉線程操作異常。我在執行asyncawait時做錯了嗎?如何使用Form.cs中的異步方法內部引發的EventArgs而不調用?

在DownloadManager.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)); 
       } 
      } 
     }); 
    } 

在Form.cs

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

我怎樣才能解決這個問題呢?

+3

最佳的解決方案是:1)使用'IProgress '和'進展',和2)使用異步的API而不是'Task.Run'。 –

回答

3

首先,除事件處理程序外,不應使用async void

其次,進度更新應通過IProgress<T>完成。這允許你使用Progress<T>是你的進度更新消費者,它爲你做線程轉換。

三,使用異步API而不是Task.Run。你可以使用HttpClient代替相當陳舊HttpWebRequest

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