DownloadDataAsync + Delegate C # работает в основном, но не в функции класса - PullRequest
1 голос
/ 31 октября 2009

Я запустил этот код человека. Это работало как главное, но когда я помещаю это в свой класс, это не работает. Зачем? Как использовать метод WebClient.DownloadDataAsync () в этом контексте?

Вылетает bc data == null и выдается исключение 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 ]

4 голосов
/ 31 октября 2009

Этот код имеет условие гонки в поле данных.Анонимный делегат DownloadDataCompleted вызывается из потока, отличного от data.Length, и в тот момент, когда вызывается DownloadDataCompleted, IsBusy становится ложным.Это гонка между двумя потоками о том, кто первым обращается к данным.Если основной поток вызывает data.Length до того, как данные заданы в потоке загрузки, вы получите исключение нулевой ссылки.Должно быть легко увидеть, если вы заставите удаление DownloadDataCompleted всегда терять гонку, добавив к нему вызов Thread.Sleep (), прежде чем он установит данные.

Состояния потока будут выглядеть следующим образом:

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

Нет способа узнать, какой поток будет запускать последнюю строку первым.На многопроцессорной машине оба могут работать одновременно.

Поскольку все это основано на времени, иногда оно будет работать, а иногда - не получится.Вам нужна какая-то синхронизация (блокировка) для всех данных, к которым обращаются многопоточные потоки.

1 голос
/ 31 октября 2009

Из-за многопоточной модели 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;
0 голосов
/ 31 октября 2009

Какой смысл использовать асинхронный метод, если вы ждете результата в цикле? Просто используйте синхронную версию:

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();
0 голосов
/ 31 октября 2009

у меня работает?

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;
...