【问题标题】:WebClient.DownloadDataAsync is freezing my UIWebClient.DownloadDataAsync 正在冻结我的 UI
【发布时间】:2010-09-21 06:27:00
【问题描述】:

我在我的 Form 构造函数中,在 InitializeComponent 之后有以下代码:

using (WebClient client = new WebClient())
{
    client.DownloadDataCompleted += new DownloadDataCompletedEventHandler(client_DownloadDataCompleted);
    client.DownloadDataAsync("http://example.com/version.txt");
}

当我启动表单时,UI 直到 client_DownloadDataCompleted 被提出后才会出现。 client_DownloadDataCompleted 方法是空的,所以没有问题。

我做错了什么? 在不冻结 UI 的情况下应该如何做到这一点?

感谢您的宝贵时间。
最好的问候。

完整代码:

Program.cs

using System;
using System.Windows.Forms;

namespace Lala
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}

Form1.cs

using System;
using System.Net;
using System.Windows.Forms;

namespace Lala
{
    public partial class Form1 : Form
    {
        WebClient client = new WebClient();

        public Form1()
        {
            client.DownloadDataCompleted += new DownloadDataCompletedEventHandler(client_DownloadDataCompleted);
            client.DownloadDataAsync(new Uri("http://www.google.com"));
            InitializeComponent();
        }

        void client_DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
        {
            textBox1.Text += "A";
        }
    }

    partial class Form1
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.button1 = new System.Windows.Forms.Button();
            this.textBox1 = new System.Windows.Forms.TextBox();
            this.SuspendLayout();
            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(12, 12);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(75, 23);
            this.button1.TabIndex = 0;
            this.button1.Text = "button1";
            this.button1.UseVisualStyleBackColor = true;
            // 
            // textBox1
            // 
            this.textBox1.Location = new System.Drawing.Point(12, 41);
            this.textBox1.Multiline = true;
            this.textBox1.Name = "textBox1";
            this.textBox1.Size = new System.Drawing.Size(468, 213);
            this.textBox1.TabIndex = 1;
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(492, 266);
            this.Controls.Add(this.textBox1);
            this.Controls.Add(this.button1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.Button button1;
        private System.Windows.Forms.TextBox textBox1;
    }
}

【问题讨论】:

  • 你的意思是DownloadData还是DownloadString?
  • 您解决了这个问题吗?
  • 分辨率为client.Proxy = null;。查看类似问题的答案:stackoverflow.com/questions/4415443/…
  • client.Proxy=null 不能解决整个问题......我遇到了同样的问题。我怀疑在 WebClient.DownloadDataAsync() 函数期间采取了某种同步操作。

标签: c# .net webclient


【解决方案1】:

现在我们已经有了完整的代码,我可以说我肯定没有看到问题 - 反正不像描述的那样。

我有一些日志记录来指示在 DownloadDataAsync 调用之前和之后,以及何时触发完成的处理程序。如果我通过 3G 下载一个大文件,在“之前”和“之后”之间会有一个暂停,但用户界面会在文件完成下载之前出现很长时间。

我怀疑 connect 是同步完成的,但实际下载是异步的。当然,这仍然很不幸 - 可能将所有这些都放到不同的线程中是要走的路 - 但如果我是对的,至少值得了解。

【讨论】:

  • 那么,将他放在不同线程上的最佳或正确方法是什么?
  • BackgroundWorker 可能是最简单的方法,这样您就可以轻松地返回 UI 线程。或者只是启动一个新线程或使用线程池。
  • 如果你是对的并且连接是同步完成的,那将是有道理的——它使得处理最常见的异常(连接失败)变得非常容易。处理异步回调的异常要复杂得多,但下载期间的异常可能比较少。
  • 另一方面,让它真正异步是一个真正的痛苦——而且你肯定必须能够在下载过程中处理异常:(
  • 我至少读过 DNS 解析是在异步 HTTP 请求触发之前同步完成的。见experts-exchange.com/Web_Development/Software/Q_21982212.html
【解决方案2】:

遇到了同样的问题,找到了解决办法。 这里相当复杂的讨论: http://social.msdn.microsoft.com/Forums/en-US/a00dba00-5432-450b-9904-9d343c11888d/webclient-downloadstringasync-freeze-my-ui?forum=ncl

简而言之,问题在于 Web 客户端正在搜索代理服务器并挂起应用程序。 以下解决方案有帮助:

WebClient webClient = new WebClient();
webClient.Proxy = null;
... Do whatever else ...

【讨论】:

  • Proxy 属性设置为空。
  • 可能这会破坏任何依赖 Windows 代理配置的人的代码。
【解决方案3】:

您想在不同的线程中运行下载,请参阅this 作为起点。

【讨论】:

  • 那么......异步是做什么用的?如果我要使用 BackgroundWorker,为什么要使用 DownloadDataAsync 而不是 DownloadData?
【解决方案4】:

我强烈怀疑这与您仍在使用 WebClient 进行异步调用时的处置有关。

尝试删除 using 语句,并改为在事件处理程序中调用 Dispose。 (或者只是为了测试,完全不用担心丢弃它。

如果您可以发布一个 short but complete program 来演示该问题,那将非常方便。

【讨论】:

  • 感谢您的代码。这很奇怪。您是否有一个比 www.google.com 需要更长时间才能回复的 URL? (这对我来说特别快,因为我在 Google 内 :)
  • 呵呵,你可以试试像cablemodem.fibertel.com.ar这样的慢速加载页面
  • @Matias:它只使用第一个请求(不是整个页面,包括图像等),所以很快。将尝试查找要下载的大文件...或通过 3G 使用我的上网本 :)
【解决方案5】:

除了处理其他人提到的可能仍在运行异步调用的东西之外,我强烈建议不要在表单的构造函数中执行此类重量级操作。

改为在 OnLoad 覆盖中执行此操作,您还可以在其中检查 DesignMode 属性,这将帮助您避免 VS 表单设计器的多个级别的地狱。

【讨论】:

  • 覆盖 OnLoad 和使用 Load 的结果相同(不同时)
【解决方案6】:

未删除:许多人都像我一样考虑 using 块,我已经确认它与 无关。

你能删除 using 块吗,我认为它正在等待处置 webclient 实例。

【讨论】:

  • 不,不使用的结果相同。
【解决方案7】:

非 UI 线程中的 DownloadDataAsync 与 DownloadData:

DownloadDataAsync 很好,因为它在处理 DownloadDataCompletedEvent 之前实际上不会占用线程,在请求发出并且服务器响应之后。

我相信 Jon Skeet 走在正确的轨道上 - 我已经读过 DNS 解析必须在异步 HTTP 请求排队并返回 DownloadDataAsync 调用之前同步完成。

DNS解析会不会很慢?

【讨论】:

    【解决方案8】:

    我刚刚在 VS2010、.NET 4 下的 WPF 项目中测试了同样的东西。

    我正在下载一个带有进度条的文件,以显示使用 WebClient.DownloadDataCompleted 等完成的百分比。

    而且,令我惊讶的是,我发现@Dan 提到的同样的事情: 在调试器中,它以一种有趣的方式阻塞线程。在调试中,我的进度表以 1% 的速度更新,然后有一段时间什么也不做,然后又突然以 100% 的速度更新。 (Debug.WriteLn 语句自始至终都能顺利打印)。在这两次之间,UI 被冻结。

    但在调试器之外,进度条从 0% 平滑移动到 100%,并且 UI 永远不会冻结。这是您所期望的。

    【讨论】:

    • 这似乎也是调试器的问题。
    【解决方案9】:

    试试这个:

    client.Proxy = GlobalProxySelection.GetEmptyProxy();
    

    【讨论】:

    • WebRequest.DefaultWebProxy = null;
    【解决方案10】:

    即使在 VS2015 中,这个问题仍然存在。我终于明白了,人们使用的代码没有问题,问题实际上是你可以多快将数据写入标签控件,这就是挂起进程并导致 UI 冻结的原因。尝试将您引用的标签替换为您的 progresschanged 处理程序中的文本框。这为我解决了 UI 中的所有滞后问题,我希望这对其他人有所帮助,因为我花了好几个小时试图弄清楚为什么代码有时会起作用,而不是其他人。

    【讨论】:

      【解决方案11】:

      这对我来说有点奇怪。

      尝试保留 WebClient 的成员 ref,这样您就不会在构造函数中销毁它,也许它在 client.Dispose() 上阻塞

      【讨论】:

      • 不,我遇到了同样的问题。
      【解决方案12】:

      using() 语句试图在 WebClient 仍在下载时调用 Dispose()。 Dispose 方法可能会等待下载完成后再继续。

      尽量不要使用 using() 语句并在您的 DownloadDataCompleted 事件中处理 WebClient。

      【讨论】:

        【解决方案13】:

        我可以正常运行您的代码。表格显示出来,下载完成后表格显示出来。

        我没有你提到的任何冻结。

        我认为这与您在其中运行它的环境有关。

        您使用的是哪个版本的 .NET/Visual Studio?

        【讨论】:

        • Visual Studio 2008 Pro (with .net 3.5) 和 Express ... 两者结果相同
        【解决方案14】:

        我已经尝试过你的代码,它运行良好。

        您能否发布您的 Main(Args[]) 方法以及运行时 a 和 b 的值:

            int a, b;
            ThreadPool.GetMaxThreads(out a, out b);
        

        我在 .NET 3.5 和 VS2008 中尝试过。我很茫然,但我相信这与您机器上的设置有关。不是代码。检查这些事情:

        • 检查线程池(上图)。我得到 a=250 b=1000
        • 禁用所有第三方插件
        • Lo​​ad VS "Clean"(你重启了吗)
        • 尽可能多地关闭程序/服务
        • 检查您的 IE 配置。我认为该类使用 IE 代码/设置
        • 防火墙?杀毒软件?
        • 在另一台电脑上试试

        【讨论】:

        • @Matias:响应者不应该提供一个完整的程序来证明问题 - 请帮助我们帮助您,让我们更容易重现。
        • 该代码对我不起作用。我在下载字符串前 1 毫秒收到 END OF APP。大约需要 5 秒才能到达“应用程序结束”
        • @Matias 我尝试了您的代码,用户界面完全响应。发布您的静态主要方法/类。
        • 如果我在 Application.Run(new Form1()); 之前使用你的代码我得到 a=250 和 b=1000。
        • - a=250, b=1000 - 没有插件,我在 VS2008 Express 上……但我在另一台机器上试过,如果我昨天安装了 VS2008 Pro,我' m 得到相同的结果。 - 没有变化。 - 我试过在 msconfig 中禁用所有
        【解决方案15】:

        嗯……我只是好奇

        你有防火墙吗?

        你的机器上有没有个防火墙?

        也许是 ZoneAlarm?

        【讨论】:

        • 没有。但我在路由器后面(使用wifi)。我没有丢包到路由器或谷歌。路由器稳定了 1 毫秒,谷歌稳定了 169 毫秒。
        【解决方案16】:

        根据我的经验,在运行调试项目(在 Visual Studio 中运行)和第一次访问服务器时,它会阻塞线程。

        运行编译后的exe时,无法感知阻塞。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-01-28
          • 2013-01-20
          • 1970-01-01
          • 2017-03-25
          • 1970-01-01
          • 1970-01-01
          • 2018-09-26
          • 2022-01-25
          相关资源
          最近更新 更多