如何在Form.cs中使用异步方法中引发的EventArgs而不调用?

时间:2017-02-20 19:48:21

标签: c# asynchronous async-await eventargs

我有一个Download方法,ProgressChangedEventArgsProgress个属性。 Download方法必须异步才能保持UI响应。所以我准备了下面的代码,但在Form.cs中调用e.Progress抛出了跨线程操作异常。我在实施asyncawait时做错了吗?

  

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

如何解决此问题?

1 个答案:

答案 0 :(得分: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);
}