DownloadDataAsync + Delegado C # obras no principal, mas não em função de classe

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

  •  22-07-2019
  •  | 
  •  

Pergunta

Eu corri este código pessoas. Ele trabalhou como principal, mas quando eu colocá-lo na minha classe ele não funciona. Por quê? Como usar o WebClient.DownloadDataAsync ( ) método neste contexto?

Ele trava bc nula dados == e lança uma exceção 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();
Foi útil?

Solução

Este código tem um corrida condição no campo de dados. O delegado DownloadDataCompleted anônimo é chamado de um thread diferente do que a chamada data.Length é feita e no ponto em que DownloadDataCompleted está sendo chamado de IsBusy se torna falsa. É uma corrida entre as duas linhas em que os dados de acesso primeiros. Se o segmento principal chama data.Length antes de dados está definido no segmento de download que você obter o seu exceção de referência nula. É preciso fácil de ver se você forçar o DownloadDataCompleted excluir para sempre perder a corrida pelo acrescentou uma chamada Thread.Sleep () para ele antes que ele define de dados.

estados do segmento será parecido com este:

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

Não há nenhuma maneira de saber qual segmento será executado a última linha em primeiro lugar. Em uma máquina processador, tanto daqueles poderia funcionar simultaneamente.

Uma vez que tudo isso é baseado em tempo, às vezes ele vai trabalhar e algumas vezes ele irá falhar. Você precisa de algum tipo de sincronização (bloqueio) de todos os dados que são acessados ??por tópicos mutliple.

Outras dicas

Devido ao modelo de segmentação de winform (como shf301 apontou), eu já modificou as códigos que funciona para mim.

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/"));
}

Há a saída:

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;

Ele funciona para mim?

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;

O que é o ponto de usar um método assíncrono se você esperar o resultado em um loop? Basta usar a versão 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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top