Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#使用秒表异步下载,但UI在某个地方冻结了_C#_Multithreading_Winforms_User Interface_Thread Safety - Fatal编程技术网

C#使用秒表异步下载,但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

下面是一些我用来下载文件,然后计算剩余时间和kbps的代码。然后,它将通过更新文本框将这些结果发布到表单上,并且它有一个进度条。我遇到的问题是UI冻结,我想这可能是我如何使用秒表,但不确定。有人有什么意见吗

    /// 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
    中,因为即使下载尚未完成,
    ProgressPercentage
    也可以为100
  • 使用异步代码时,最好在调用异步方法之前而不是之后初始化状态变量(在您的情况下,
    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
    中,因为即使下载尚未完成,
    ProgressPercentage
    也可以为100
  • 使用异步代码时,最好在调用异步方法之前而不是之后初始化状态变量(在您的情况下,
    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冻结。建议的答案似乎是一个同样适用于您的解决方案。谢谢,实际上我已经在下载完成时做了更改。我使用的是一个带计数器的系统,但这看起来也能很好地工作。谢谢谢谢,实际上我已经在下载完成时做了更改。我使用的是一个带计数器的系统,但这看起来也能很好地工作。谢谢