DownloadDataAsync + Delegate C#在main函数中工作,但不在类函数中工作

时间:2009-10-31 01:32:37

标签: .net webclient

我跑了这个人代码。它作为主要工作,但当我把它放在我的课堂,它不起作用。为什么? 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();

4 个答案:

答案 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();