C#使用秒表异步下载,但UI在某个地方冻结了
下面是一些我用来下载文件,然后计算剩余时间和kbps的代码。然后,它将通过更新文本框将这些结果发布到表单上,并且它有一个进度条。我遇到的问题是UI冻结,我想这可能是我如何使用秒表,但不确定。有人有什么意见吗C#使用秒表异步下载,但UI在某个地方冻结了,c#,multithreading,winforms,user-interface,thread-safety,C#,Multithreading,Winforms,User Interface,Thread Safety,下面是一些我用来下载文件,然后计算剩余时间和kbps的代码。然后,它将通过更新文本框将这些结果发布到表单上,并且它有一个进度条。我遇到的问题是UI冻结,我想这可能是我如何使用秒表,但不确定。有人有什么意见吗 /// Downloads the file. private void Download_Begin() { web_client = new System.Net.WebClient(); web_client.DownloadPr
/// Downloads the file.
private void Download_Begin()
{
web_client = new System.Net.WebClient();
web_client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(Download_Progress);
web_client.DownloadFileCompleted += new AsyncCompletedEventHandler(Download_Complete);
stop_watch = new System.Diagnostics.Stopwatch();
stop_watch.Start();
try
{
if (Program.Current_Download == "Install_Client.exe")
{
web_client.DownloadFileAsync(new Uri("http://www.website.com/Client/Install_Client.exe"), @"C:\Downloads\Install_Client.exe");
}
else
{
web_client.DownloadFileAsync(new Uri((string.Format("http://www.website.com/{0}", Program.Current_Download))), (string.Format(@"C:\Downloads\{0}", Program.Current_Download)));
}
}
catch(Exception)
{
stop_watch.Stop();
}
Program.Downloading = true;
Download_Success = false;
}
/// -------------------
/// Tracks download progress.
private void Download_Progress(object sender, DownloadProgressChangedEventArgs e)
{
double bs = e.BytesReceived / stop_watch.Elapsed.TotalSeconds;
this.label_rate.Text = string.Format("{0} kb/s", (bs / 1024d).ToString("0.00"));
long bytes_remaining = e.TotalBytesToReceive - e.BytesReceived;
double time_remaining_in_seconds = bytes_remaining / bs;
var remaining_time = TimeSpan.FromSeconds(time_remaining_in_seconds);
string hours = remaining_time.Hours.ToString("00");
if (remaining_time.Hours > 99)
{
hours = remaining_time.Hours.ToString("000");
}
this.time_remaining.Text = string.Format("{0}::{1}::{2} Remaining", hours, remaining_time.Minutes.ToString("00"), remaining_time.Seconds.ToString("00"));
progressBar1.Maximum = (int)e.TotalBytesToReceive / 100;
progressBar1.Value = (int)e.BytesReceived / 100;
if (e.ProgressPercentage == 100)
{
Download_Success = true;
}
}
/// -------------------------
UI线程可能由于各种原因而冻结,尽管您正在调用异步下载函数。防止UI冻结的一种方法是调用从不同于UI的线程下载文件。例如,您可以通过使用BeginInvoke()调用或简单地包装在非UI线程中执行的代码来实现这一点,并安全地修改表单的控件
UI线程可能由于各种原因而冻结,尽管您正在调用异步下载函数。防止UI冻结的一种方法是调用从不同于UI的线程下载文件。例如,您可以通过使用BeginInvoke()调用或简单地包装在非UI线程中执行的代码来实现这一点,并安全地修改表单的控件
关于代码的一些想法:
- 当异常发生时,不需要关闭秒表,秒表内部不做任何工作,它只需在启动秒表时记住当前时间,并在访问经过的时间时计算差值
- 捕获所有异常时,不需要提供异常类(即
而不是catch
)catch(Exception)
- 仅在
事件中将下载标记为已完成,而不是在DownloadFileCompleted
中,因为即使下载尚未完成,DownloadProgressChanged
也可以为100ProgressPercentage
- 使用异步代码时,最好在调用异步方法之前而不是之后初始化状态变量(在您的情况下,
和Download\u Success
)Program.Downloading
DownloadProgreesChanged
经常会被WebClient
触发,因此UI线程可能会被更新消息淹没。您需要拆分报告进度并更新UI代码。UI应该以定时方式更新,例如,每秒两次。非常粗略的代码示例如下:
// Put timer on your form, equivalent to:
// Update_Timer = new System.Windows.Forms.Timer();
// Update_Timer.Interval = 500;
// Update_Timer.Tick += Timer_Tick;
private void Download_Begin()
{
web_client = new System.Net.WebClient();
web_client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(Download_Progress);
web_client.DownloadFileCompleted += new AsyncCompletedEventHandler(Download_Complete);
Program.Downloading = true;
Download_Success = false;
stop_watch = System.Diagnostics.Stopwatch.StartNew();
Update_Timer.Start();
web_client.DownloadFileAsync(new Uri("uri"), "path");
}
private int _Progress;
private void Download_Progress(object sender, DownloadProgressChangedEventArgs e)
{
_Progress = e.ProgressPercentage;
}
private void Download_Complete(object sender, AsyncCompletedEventArgs e)
{
Update_Timer.Stop();
Program.Downloading = false;
Download_Success = true;
}
private void Timer_Tick(object sender, EventArgs e)
{
// Your code to update remaining time and speed
// this.label_rate.Text = ...
// this.time_remaining.Text = ...
progressBar1.Value = _Progress;
}
关于代码的一些想法:
- 当异常发生时,不需要关闭秒表,秒表内部不做任何工作,它只需在启动秒表时记住当前时间,并在访问经过的时间时计算差值
- 捕获所有异常时,不需要提供异常类(即
而不是catch
)catch(Exception)
- 仅在
事件中将下载标记为已完成,而不是在DownloadFileCompleted
中,因为即使下载尚未完成,DownloadProgressChanged
也可以为100ProgressPercentage
- 使用异步代码时,最好在调用异步方法之前而不是之后初始化状态变量(在您的情况下,
和Download\u Success
)Program.Downloading
DownloadProgreesChanged
经常会被WebClient
触发,因此UI线程可能会被更新消息淹没。您需要拆分报告进度并更新UI代码。UI应该以定时方式更新,例如,每秒两次。非常粗略的代码示例如下:
// Put timer on your form, equivalent to:
// Update_Timer = new System.Windows.Forms.Timer();
// Update_Timer.Interval = 500;
// Update_Timer.Tick += Timer_Tick;
private void Download_Begin()
{
web_client = new System.Net.WebClient();
web_client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(Download_Progress);
web_client.DownloadFileCompleted += new AsyncCompletedEventHandler(Download_Complete);
Program.Downloading = true;
Download_Success = false;
stop_watch = System.Diagnostics.Stopwatch.StartNew();
Update_Timer.Start();
web_client.DownloadFileAsync(new Uri("uri"), "path");
}
private int _Progress;
private void Download_Progress(object sender, DownloadProgressChangedEventArgs e)
{
_Progress = e.ProgressPercentage;
}
private void Download_Complete(object sender, AsyncCompletedEventArgs e)
{
Update_Timer.Stop();
Program.Downloading = false;
Download_Success = true;
}
private void Timer_Tick(object sender, EventArgs e)
{
// Your code to update remaining time and speed
// this.label_rate.Text = ...
// this.time_remaining.Text = ...
progressBar1.Value = _Progress;
}
我没有看到任何会导致冻结的代码。在你的程序中使用探查器,几乎每个程序都有一个功能可以过滤到UI冻结代码。@ScottChamberlain好的,看起来文件的实际下载,异步下载有很高的cpu使用率。不知道为什么会这样。大卫,请参考前面的问题。下载进度的回调似乎经常被调用。由于您正在该回调中进行一些UI更新,因此可能会导致UI冻结。建议的答案似乎是一个同样适用于您的解决方案。我没有看到任何代码会导致冻结。在你的程序中使用探查器,几乎每个程序都有一个功能可以过滤到UI冻结代码。@ScottChamberlain好的,看起来文件的实际下载,异步下载有很高的cpu使用率。不知道为什么会这样。大卫,请参考前面的问题。下载进度的回调似乎经常被调用。由于您正在该回调中进行一些UI更新,因此可能会导致UI冻结。建议的答案似乎是一个同样适用于您的解决方案。谢谢,实际上我已经在下载完成时做了更改。我使用的是一个带计数器的系统,但这看起来也能很好地工作。谢谢谢谢,实际上我已经在下载完成时做了更改。我使用的是一个带计数器的系统,但这看起来也能很好地工作。谢谢