我有一个Download
方法,ProgressChangedEventArgs
有Progress
个属性。 Download
方法必须异步才能保持UI响应。所以我准备了下面的代码,但在Form.cs
中调用e.Progress
抛出了跨线程操作异常。我在实施async
和await
时做错了吗?
在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");
}
如何解决此问题?
答案 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);
}