DownloadDataAsync + Delegate C # funciona en la función principal pero no en la clase

StackOverflow https://stackoverflow.com/questions/1653108

  •  22-07-2019
  •  | 
  •  

Pregunta

Ejecuté este código de personas. Funcionó como principal pero cuando lo puse en mi clase no funciona. ¿Por qué? Cómo usar el WebClient.DownloadDataAsync ( ) método en este contexto?

Se bloquea bc data == null y genera una excepción nula :(.

public class Test2
    {
        public void func()
        {
            byte[] data = null;
            WebClient client = new WebClient();
            client.DownloadDataCompleted +=
                delegate(object sender, DownloadDataCompletedEventArgs e)
                {
                    data = e.Result;
                };
            Console.WriteLine("starting...");
            client.DownloadDataAsync(new Uri("https://stackoverflow.com/questions/"));
            while (client.IsBusy)
            {
                Console.WriteLine("\twaiting...");
                Thread.Sleep(100);
            }
            Console.WriteLine("done. {0} bytes received;", data.Length);
        }
    }

//i tried calling on form_load and a button click
new Test2().func();
¿Fue útil?

Solución

Este código tiene una condición de carrera en el campo de datos. El delegado anónimo DownloadDataCompleted se llama desde un hilo diferente al de los datos. Se realiza una llamada de longitud y en el momento en que se llama DownloadDataCompleted, IsBusy se vuelve falso. Es una carrera entre los dos hilos sobre quién accede primero a los datos. Si el hilo principal llama a datos. Longitud antes de que los datos se establezcan en el hilo de descarga, obtiene su excepción de referencia nula. Es fácil ver si fuerza la eliminación de DownloadDataCompleted para perder siempre la carrera agregando una llamada Thread.Sleep () antes de que establezca los datos.

Los estados del hilo se verán así:

Main Thread             Download Thread     client.IsBusy
Waiting....             downloading...      true
leaves waiting loop     calls delegate      false
calls data.Length       data = e.Result

No hay forma de saber qué hilo ejecutará la última línea primero. En una máquina multiprocesador, ambos podrían ejecutarse simultáneamente.

Como todo esto se basa en el tiempo, a veces funcionará y otras fallará. Necesita algún tipo de sincronización (bloqueo) en todos los datos a los que acceden múltiples hilos.

Otros consejos

Debido al modelo de subprocesos de winform (como señaló shf301 ), he modificado los códigos que funcionan para mí.

private void button1_Click(object sender, EventArgs e)
{
    func();
}
void func()
{
    WebClient client = new WebClient();
    byte[] data = null;
    long rcv = 0; //last number of bytes received

    //check data received for progress
    client.DownloadProgressChanged += delegate(object sender, DownloadProgressChangedEventArgs e)
    {
        if (e.BytesReceived - rcv > 1000)
        {
            Console.WriteLine("\tBytes Received: " + e.BytesReceived.ToString());
            rcv = e.BytesReceived;
        }
        //else don't report
        Thread.Sleep(1);
    };
    client.DownloadDataCompleted +=
        delegate(object sender, DownloadDataCompletedEventArgs e)
        {
            data = e.Result;
            Console.WriteLine("done. {0} bytes received;", data.Length);
        };
    Console.WriteLine("starting...");

    //fire and forget
    client.DownloadDataAsync(new Uri("http://stackoverflow.com/questions/"));
}

Ahí está la salida:

starting...
    Bytes Received: 8192
    Bytes Received: 11944
    Bytes Received: 15696
    Bytes Received: 20136
    Bytes Received: 24232
    Bytes Received: 28040
    Bytes Received: 32424
    Bytes Received: 36176
    Bytes Received: 40616
    Bytes Received: 44712
    Bytes Received: 48269
done. 48269 bytes received;

¿Funciona para mí?

C:\TEMP\ConsoleApplication5\bin\Debug>ConsoleApplication5.exe
starting...
    waiting...
    waiting...
    waiting...
    waiting...
    waiting...
    waiting...
    waiting...
    waiting...
    waiting...
    waiting...
    waiting...
    waiting...
    waiting...
    waiting...
    waiting...
    waiting...
    waiting...
    waiting...
    waiting...
    waiting...
    waiting...
    waiting...
    waiting...
    waiting...
    waiting...
    waiting...
done. 48178 bytes received;

¿Cuál es el punto de usar un método asíncrono si espera el resultado en un bucle? Solo usa la versión síncrona:

public class Test2
    {
        public void func()
        {
            WebClient client = new WebClient();
            byte[] data = client.DownloadData(new Uri("http://stackoverflow.com/questions/"));
            Console.WriteLine("done. {0} bytes received;", data.Length);
        }
    }

//i tried calling on form_load and a button click
new Test2().func();
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top