DownloadDataAsync + Delegate C#はmainで機能しますが、クラス関数では機能しません

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はfalseになります。データに最初にアクセスする2つのスレッド間の競合です。ダウンロードスレッドでデータが設定される前にメインスレッドがdata.Lengthを呼び出すと、null参照例外が発生します。 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