Pregunta

Tengo en mi constructor de formularios, después de InitializeComponent el siguiente código:

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

Cuando inicio mi formulario, la IU no aparece hasta que se inicia client_DownloadDataCompleted. El método client_DownloadDataCompleted está vacío, por lo que no hay ningún problema allí.

¿Qué estoy haciendo mal? ¿Cómo se supone que debe hacer esto sin congelar la interfaz de usuario?

Gracias por su tiempo.
Saludos cordiales.

CÓDIGO COMPLETO:

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;
    }
}
¿Fue útil?

Solución

Ahora que tenemos el código completo, puedo decir que definitivamente no veo el problema, de todos modos, no exactamente como se describe.

Tengo un poco de registro para indicar justo antes y después de las llamadas DownloadDataAsync, y cuándo se activa el controlador completo. Si descargo un archivo grande a través de 3G, hay una pausa entre " antes de " y '' después de '' pero la IU aparece años antes de que el archivo complete la descarga.

Tengo la sospecha de que connect se realiza sincrónicamente, pero la descarga real es asincrónica. Eso todavía es desafortunado, por supuesto, y posiblemente poner todo eso en un hilo diferente es el camino a seguir, pero si tengo razón, al menos vale la pena saberlo.

Otros consejos

Encontró el mismo problema y encontró una solución. Discusión bastante compleja aquí: http://social.msdn.microsoft.com/Forums/en-US/a00dba00-5432-450b-9904-9d343c11888d/webclient-downloadstringasync-freeze-my-ui?forum=ncl

En resumen, el problema es que el cliente web busca servidores proxy y bloquea la aplicación. La siguiente solución ayuda:

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

Si desea ejecutar la descarga en un hilo diferente, consulte esto como punto de partida.

Sospecho firmemente que tiene que ver con deshacerse del WebClient mientras todavía lo está utilizando para una llamada asincrónica.

Intente eliminar la instrucción using y llame a Dispose en un controlador de eventos. (O simplemente para probar, no se preocupe por deshacerse de él en absoluto.

Si pudiera publicar un programa corto pero completo que demuestra el problema, eso sería realmente útil.

Además de deshacerse de algo que posiblemente todavía está ejecutando la llamada asincrónica que otras personas han mencionado, recomendaría encarecidamente no hacer cosas pesadas como esta en el constructor de un formulario.

En su lugar, hágalo en una anulación de OnLoad, donde también podrá verificar la propiedad DesignMode que lo ayudará a evitar varios niveles del infierno con el diseñador de formularios VS.

UNDELETED: Como muchos piensan en el bloque de uso como lo hago yo, he confirmado que está no relacionado.

¿Puede eliminar el bloque de uso? Creo que está esperando deshacerse de la instancia del cliente web.

DownloadDataAsync vs. DownloadData en un hilo sin interfaz de usuario:

DownloadDataAsync es bueno porque en realidad no ata un hilo hasta que maneja el DownloadDataCompletedEvent, después de que se haya realizado la solicitud y el servidor responda.

Creo que Jon Skeet está en el camino correcto: he leído que la resolución de DNS debe completarse sincrónicamente antes de que la solicitud HTTP asíncrona se ponga en cola y vuelva la llamada DownloadDataAsync.

¿Podría la resolución DNS ser lenta?

Acabo de probar lo mismo en un proyecto WPF en VS2010, .NET 4.

Estoy descargando un archivo con una barra de progreso para mostrar el porcentaje completado usando WebClient.DownloadDataCompleted, etc.

Y, para mi sorpresa, encuentro lo mismo que @Dan mencionó: Dentro del depurador bloquea el hilo de una manera divertida. En la depuración, mi medidor de progreso se actualiza al 1%, luego no hace nada por un tiempo, luego se actualiza de nuevo repentinamente al 100%. (Las declaraciones Debug.WriteLn se imprimen sin problemas en todo momento). Y entre estas dos veces, la IU está congelada.

Pero fuera del depurador, la barra de progreso se mueve sin problemas del 0% al 100%, y la IU nunca se congela. Que es lo que esperarías.

prueba esto:

client.Proxy = GlobalProxySelection.GetEmptyProxy();

Eso me parece un poco raro.

Intente mantener un miembro ref del WebClient para que no lo destruya en el constructor, tal vez esté bloqueando el cliente. Dispose ()

La instrucción using () está intentando llamar a Dispose () del WebClient mientras aún se está descargando. El método Dispose probablemente espera a que finalice la descarga antes de continuar.

Intente no usar una instrucción using () y deseche el WebClient en su evento DownloadDataCompleted.

Puedo ejecutar tu código bien. Y el formulario aparece y la descarga se completa DESPUÉS de que apareció el formulario.

No tengo ninguna congelación como usted mencionó.

Creo que tiene algo que ver con el entorno en el que lo está ejecutando.

¿En qué versión de .NET / Visual Studio estás?

He probado tu código y funciona bien.

¿Podría publicar su método Main (Args []) y el valor de ayb cuando se ejecuta esto:

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

Lo probé en .NET 3.5 y VS2008. Estoy perdido, pero estoy convencido de que tiene que ver con la configuración de su máquina. No es el código. Comprueba estas cosas:

  • Compruebe el grupo de subprocesos (arriba). Obtengo a = 250 b = 1000
  • Deshabilitar todos los complementos de terceros
  • Cargar VS " Limpiar " (¿Has reiniciado)
  • Cierra tantos programas / servicios como puedas
  • Verifique su configuración de IE. Creo que esa clase usa el código / configuración de IE
  • Firewall? ¿Antivirus?
  • Pruébelo en otra computadora

Ummm ... solo tengo curiosidad

¿Tiene algún firewall activado?

¿

algún cortafuegos en su máquina?

¿Quizás ZoneAlarm?

En mi experiencia, bloquea el hilo cuando se ejecuta la depuración del proyecto (se ejecuta dentro de Visual Studio) y cuando se accede al servidor por primera vez.

Al ejecutar el exe compilado, el bloqueo no es perceptible.

Este problema todavía está en curso incluso en VS2015. Finalmente descubrí esto, no hay nada de malo en el código que la gente está usando, el problema es en realidad qué tan rápido puede escribir datos en un control de etiqueta y esto es lo que bloquea el proceso y hace que su IU se congele. Intente reemplazar las etiquetas a las que hace referencia con cuadros de texto en sus controladores de cambios de progreso. Esto resolvió todos los retrasos en la interfaz de usuario para mí, espero que esto ayude a otros, ya que pasé horas tratando de descubrir por qué el código funcionaba a veces y no a otros.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top