13

プロダクションコードで次の方法を使用します。

private void DownloadData(Uri uri)
{
    WebClient webClient = new WebClient();
    DownloadDataCompletedEventHandler eh = null;
    eh = delegate(object sender, DownloadDataCompletedEventArgs e)
        {
            webClient.DownloadDataCompleted -= eh;
            ((IDisposable) webClient).Dispose();
            OnDataDownloaded();
        };
    webClient.DownloadDataCompleted += eh;
    webClient.DownloadDataAsync(uri);
}

イベントが呼び出されるWebClient前にインスタンスがガベージコレクションされることで、再現が難しいバグが発生する可能性があるのではないかと心配しています。メソッドを終了した後、オブジェクトへの明確な参照がないため、おそらく発生する可能性があります。DownloadDataCompletedDownloadData()WebClient

だから私の質問は:これは現実的に起こり得るのか?問題を再現できないため、WebClientオブジェクトがガベージコレクションされないようにする内部的な問題が発生している可能性があります(たとえば、オブジェクトが応答を待っている間にどこかでグローバルオブジェクトに登録される可能性があります)。

違いがあれば、コードは.NET2.0で実行されています。

4

5 に答える 5

12

いいえ、コールバックが完了するまで、オブジェクトは GC されません。ガベージ コレクターは、.NET での非同期呼び出し中に参照されていないオブジェクトを一時的に破棄しますか? によると、, "非同期 API はリクエストへの参照を (非同期 IO 操作が記録されるスレッド プール内に) 保持するため、完了するまでガベージ コレクションは行われません。 "

ただし、コードは必要のないことも実行しています。イベント ハンドラーをデタッチする必要はなく、Web クライアントで Dispose を呼び出す必要もありません。(Dispose() は実際には WebClient によって実装されていません。これは、 http://referencesource.microsoft.com/netframework.aspxの .NET Framework リファレンス ソースで確認できます)。

したがって、実際にはコールバックで webclient インスタンスを参照する必要はありません。つまり、次のコードも同様に機能し、デリゲート内から外部ローカル変数を参照する潜在的な問題 (上記で説明) を回避します。

private void DownloadData(Uri uri)
{
    WebClient webClient = new WebClient();
    DownloadDataCompletedEventHandler eh = null;
    eh = delegate(object sender, DownloadDataCompletedEventArgs e)
    {
        OnDataDownloaded();
    };
    webClient.DownloadDataCompleted += eh;
    webClient.DownloadDataAsync(uri);
}

いずれにしても、バグの原因を別の場所で探した方がよいでしょう。私が見たいのは、HTTP 呼び出しの結果です。メモリが不足している、サーバー エラーが発生しているなどの可能性があります。e.Error を調べて、呼び出しが実際に機能しているかどうかを確認できます。

于 2009-08-14T19:31:21.660 に答える
5

内部参照がある可能性があるため、非同期操作の進行中に通常ガベージコレクションできるかどうかはわかりませんWebClientが、より大きな問題は次のとおりです。

WebClientリクエストを処理してハンドラーを呼び出すのに十分な「生きている」ままである限り、メインWebClientオブジェクト自体がガベージコレクションされるかどうかは問題ですか?

ドキュメントには、WebClient参照を保持する必要があることについては何も言及されていないため (たとえば、System.Threading.Timerdocsとは異なります)、これで問題ないと考えるのが妥当だと思います。

この特定のケースでは、デリゲートは への参照を持っているWebClientため、デリゲート自体が参照されている限り、 は参照WebClientできません。私の推測では、ネットワーク トラフィックが到着したときに何をすべきかを知るために、システムのどこかの部分でコールバックを保持する必要があり、そのコールバックは最終的に (間接的に) デリゲートにつながるので、問題ありません。

于 2009-06-11T07:41:03.317 に答える
1

Debugging Tools for Windows を使用してアプリケーションのデバッグを試すことができます。これにより、(適切なプラグインを使用して)特定のオブジェクトへの参照を保持しているものを正確に確認できます。このような場合に非常に便利です。

しかし、あなたの質問に対する答えはわかりません。1 つの可能性は、操作中に WebClient が自分自身を「ルート」オブジェクトの 1 つにし、ガベージ コレクションが行われないことです (.NET アプリケーションには通常、使用されるいくつかの参照ツリーのルートであるそのようなオブジェクトが約 5 ~ 10 個あります)。アプリケーションによって)。ただし、これは純粋な憶測です。

于 2009-06-11T08:09:32.633 に答える
0

スコープ変数(この場合はwebClient)への参照を使用して匿名メソッドを作成し、そのオブジェクトへの参照を使用して独自の変数を作成する場合。したがって、jonが推測しているように、デリゲートはwebClientへの参照を保持し、デリゲートが登録を解除する前に、webClientをガベージコレクションすることはできません。

ただし、通常、デリゲートメソッドでwebClient参照を使用するのではなく、送信者を内部変数にキャストすることをお勧めします。匿名メソッドの外部で変数を使用すると、実際にいくつかの非常に奇妙なバグが発生する可能性があります。

于 2009-06-11T10:17:56.343 に答える
0

コインの裏側...WebClientどこかに参照を保存した場合、それが違いを生むかどうかを確認するためだけに...それで問題は完全に解消されますか?この方法で確認する方が、論理的に見えるものを推測するよりも簡単な場合があります。

于 2009-06-11T07:49:40.863 に答える