DownloadDataAsync + Delegierter C # funktioniert in Haupt aber nicht in Klassenfunktion

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

  •  22-07-2019
  •  | 
  •  

Frage

Ich lief diese Personen Code. Es funktionierte als Haupt, aber wenn ich es in meiner Klasse setzen es funktioniert nicht. Warum? Wie die WebClient.DownloadDataAsync verwenden ( ) Verfahren in diesem Zusammenhang?

Es stürzt bc Daten == null und löst eine Null Ausnahme: (

.
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();
War es hilfreich?

Lösung

Dieser Code hat ein race condition auf dem Datenfeld rel="nofollow. Die anonym DownloadDataCompleted Delegierten werden von einem anderen Thread aufgerufen als der data.Length Anruf getätigt wird, und an dem Punkt, dass die DownloadDataCompleted IsBusy falsch werden aufgerufen werden. Es ist ein Rennen zwischen den beiden Threads auf die Daten zuerst zugreifen. Wenn der Haupt-Thread data.Length aufruft, bevor die Daten auf den Download-Thread gesetzt bekommen Sie Ihre NULL-Verweis Ausnahme. Es ist ein Muss leicht zu sehen, wenn Sie die DownloadDataCompleted löschen zwingen immer das Rennen zu verlieren, indem hinzugefügt einem Thread.Sleep () aufrufen, um ihn, bevor er Datensätze.

Die Staaten Thread wird wie folgt aussehen:

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

Es gibt keine Möglichkeit, zu wissen, welcher Thread zuerst die letzte Zeile ausgeführt wird. Auf einer Multi-Prozessor-Maschine könnte diese beide gleichzeitig ausgeführt werden.

Da dies alles auf Timing basiert manchmal funktioniert es und manchmal wird es scheitern. Sie brauchen eine Art der Synchronisation (Verriegelung) auf alle Daten, die von mutliple Threads zugegriffen wird.

Andere Tipps

Durch das Threading-Modell von winform (wie shf301 darauf hingewiesen), habe ich die Codes geändert, die für mich funktioniert.

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/"));
}

Es gibt die Ausgabe:

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;

Es funktioniert für mich?

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;

Was ist der Sinn der Verwendung einer Asynchron-Methode, wenn Sie in einer Schleife auf das Ergebnis warten? Verwenden Sie einfach die synchrone Version:

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();
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top