Reputation: 3005
I have a Download
method and ProgressChangedEventArgs
carrying Progress
property. The Download
method must async to keep UI responsive. So I prepared the code below but in Form.cs
calling e.Progress
throws cross thread operation exception. Am I doing wrong at implementing async
and await
?
In 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));
}
}
});
}
In 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");
}
How can I fix this problem?
Upvotes: 0
Views: 316
Reputation: 457302
First, you shouldn't use async void
except for event handlers.
Second, progress updates should be done via IProgress<T>
. This permits you to use Progress<T>
is your progress update consumer, which does thread transitions for you.
Third, use asynchronous APIs instead of Task.Run
. You could use HttpClient
instead of the rather dated 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);
}
}
}
Usage:
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);
}
Upvotes: 3