我跑了这个人代码。它作为主要工作,但当我把它放在我的课堂,它不起作用。为什么? How to use the WebClient.DownloadDataAsync() method in this context?
它崩溃bc data == null并抛出一个空异常:(。
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();
答案 0 :(得分:4)
此代码在数据字段上有race condition。 DownloadDataCompleted匿名委托是从与data.Length调用不同的线程调用的,并且在调用DownloadDataCompleted时,IsBusy变为false。这是两个线程之间的竞争,谁首先访问数据。如果主线程在下载线程上设置数据之前调用data.Length,则会得到空引用异常。通过在设置数据之前添加一个Thread.Sleep()调用来强制DownloadDataCompleted删除总是松开竞争,这一点很容易看出来。
线程的状态如下所示:
Main Thread Download Thread client.IsBusy
Waiting.... downloading... true
leaves waiting loop calls delegate false
calls data.Length data = e.Result
无法知道哪个线程将首先运行最后一行。在多处理器机器上,两者都可以同时运行。
由于这一切都基于时间,有时它会起作用,有时会失败。您需要对多个线程访问的所有数据进行某种同步(锁定)。
答案 1 :(得分:1)
由于winform的线程模型(指出shf301
),我修改了适用于我的代码。
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/"));
}
有输出:
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;
答案 2 :(得分:0)
对我有用吗?
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;
答案 3 :(得分:0)
如果在循环中等待结果,使用异步方法有什么意义?只需使用同步版本:
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();