DownloadDataAsync + Delegado C # obras no principal, mas não em função de classe
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();
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();