3

私は次のコードを持っています

ThreadPool.QueueUserWorkItem(new WaitCallback(DownloadAsync), apiMethod);
downloadHandle.WaitOne();

DownloadAsync の場所

private void DownloadAsync(object _uri)
        {
            var url = _uri as string;
            WebClient client = new WebClient();
            client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);
            client.DownloadStringAsync(new Uri(GLOBALS.MAIN_API_URL + url, UriKind.Absolute));
        }

void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            result = e.Result;
            downloadHandle.Set();
        }

したがって、私の問題は、 downloadHandle. Set() が呼び出されないことです。しかし、私はなぜ理解していないのですか?私は DownloadAsync の新しいスレッドを作成し、downloadHandle.WaitOne() は彼をブロックすべきではありません。

私が必要とするのは、Async の代わりに Sync メソッドを作成することです。

ありがとう!

UPD: 非同期呼び出しでソース コードを更新しました。

4

4 に答える 4

4

client.DownloadString同期メソッドであるため、完了したハンドラーが呼び出されることはありません。非同期バージョンを呼び出す必要があります:client.DownloadStringAsync()

msdn でDownloadStringAsyncの詳細を読むことができます。コードを呼び出す必要があるという事実に依存している場合は、コードを try-catch ブロックに入れて例外を処理することも賢明です。

コードは次のようになります。

private void DownloadAsync(object _uri)
{
    try
    {
        var url = _uri as string;
        WebClient client = new WebClient();
        client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);
        client.DownloadStringAsync(new Uri(GLOBALS.MAIN_API_URL + url, UriKind.Absolute));
    }
    catch //appropriate exception
    {
       //Handle exception (maybe set downloadHandle or report an error)
    }
}

void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
    result = e.Result;
    downloadHandle.Set();
}
于 2012-02-20T09:51:22.947 に答える
2

私の推測downloadHandle.WaitOne()では、UI スレッドから呼び出しています。UI イベント ハンドラーからコードを実行している場合 (たとえば、ボタンのクリックや新しいページへのナビゲーション)、またはイベント ハンドラーによって呼び出された関数を実行している場合は、UI スレッドにいます。

なぜこれが問題なのですか?

案の定DownloadAsync、スレッドプールのおかげでバックグラウンドで実行されます。ただし、WebClient クラスは常に UI スレッドを使用してそのコールバック (つまり、client_DownloadStringCompletedメソッド) を実行します... しかし、このまったく同じスレッドがdownloadHandle.WaitOne()!によってブロックされます。

そのため、ロックにタイムアウトを設定すると、client_DownloadStringCompletedメソッドが魔法のように実行されます。

これを修正する方法は?2 つのソリューション。

downloadHandle.WaitOne()1/メインスレッドでの実行を停止します。ユーザー インターフェイスがブロックされ、アプリケーションが応答しなくなります。これは決して良いことではありません。

2/HttpWebRequestの代わりに を使用しWebClientます。HttpWebRequestダウンロードを開始したのと同じスレッドでコールバックを実行するため、このデッドロックの問題は発生しません。

于 2012-02-20T11:07:48.793 に答える
2

完了コールバック メソッドの呼び出しを妨げる例外が発生している可能性があります。それがまったく呼び出されたかどうかを確認しましたか?

ところで、ここで実際にスレッド プールを使用する必要はありません。ブロックしないため、メイン スレッドで DownloadAsync() を呼び出すことができます。

于 2012-02-20T09:51:56.867 に答える
0

ブロックdownloadHandle.WaitOne();から呼び出すと、呼び出されることはありません。これを背景に移動します。UIUI threadThreadPool

ThreadPool.QueueUserWorkItem(new WaitCallback(DownloadAsync), apiMethod);
downloadHandle.WaitOne();
于 2012-02-20T09:52:41.700 に答える