DownloadDataAsync + Delegate C # работает в основном, но не в функции класса

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

  •  22-07-2019
  •  | 
  •  

Вопрос

Я запустил этот код лица. Он работал как основной, но когда я положил его в своем классе, он не работает. Зачем? Как использовать 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();
Это было полезно?

Решение

Этот код имеет условие гонки в поле данных. Анонимный делегат 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

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

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

Другие советы

Из-за многопоточной модели 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;

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

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;

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

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();
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top