384

.NET Framework 4.5 のSystem.Net.Http.HttpClientSystem.Net.Http.HttpClientHandlerは、( System.Net.Http.HttpMessageInvokerを介して) IDisposable を実装します。

usingステートメントのドキュメントには次のように記載されています。

原則として、IDisposable オブジェクトを使用する場合は、using ステートメントで宣言してインスタンス化する必要があります。

この回答では、次のパターンを使用しています。

var baseAddress = new Uri("http://example.com");
var cookieContainer = new CookieContainer();
using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
{
    var content = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("foo", "bar"),
        new KeyValuePair<string, string>("baz", "bazinga"),
    });
    cookieContainer.Add(baseAddress, new Cookie("CookieName", "cookie_value"));
    var result = client.PostAsync("/test", content).Result;
    result.EnsureSuccessStatusCode();
}

Dispose()しかし、Microsoft からの最も目に見える例は、明示的または暗黙的に呼び出されません。例えば:

発表のコメントで、誰かが Microsoft の従業員に次のように尋ねました。

サンプルを確認したところ、HttpClient インスタンスで破棄アクションを実行していないことがわかりました。アプリで using ステートメントを使用して HttpClient のすべてのインスタンスを使用しましたが、HttpClient は IDisposable インターフェイスを実装しているため、これが正しい方法だと思いました。私は正しい道を進んでいますか?

彼の答えは次のとおりです。

一般的には正しいですが、.Net 4 では実際には混在しないため、「using」と async には注意する必要があります。.Net 4.5 では、「using」ステートメント内で「await」を使用できます。

ところで、同じ HttpClient を好きなだけ何度でも再利用できるので、通常、それらを常に作成/破棄することはありません。

2 番目の段落は、この質問には不要です。これは、HttpClient インスタンスを何回使用できるかについてではなく、不要になった後に破棄する必要があるかどうかについてです。

(更新: 実際、@DPeden によって以下に提供されているように、2 番目の段落が答えの鍵です。)

だから私の質問は:

  1. 現在の実装 (.NET Framework 4.5) では、HttpClient および HttpClientHandler インスタンスで Dispose() を呼び出す必要がありますか? 明確化: 「必要」とは、リソースの漏洩やデータ破損のリスクなど、処分しないことによる悪影響があるかどうかを意味します。

  2. IDisposable を実装しているので、必要がない場合はとにかく「良い習慣」でしょうか?

  3. 必要な場合 (または推奨される場合)、上記のコードは安全に実装されていますか (.NET Framework 4.5 の場合)?

  4. これらのクラスが Dispose() を呼び出す必要がない場合、なぜ IDisposable として実装されたのでしょうか?

  5. 必要な場合、または推奨される方法である場合、Microsoft の例は誤解を招くものまたは安全でないものですか?

4

12 に答える 12

285

一般的なコンセンサスは、HttpClient を破棄する必要はない (すべきではない) ということです。

それが機能する方法に密接に関与している多くの人々がこれを述べています.

Darrel Miller のブログ投稿と関連する SO 投稿を参照してください: HttpClient のクロール結果がメモリ リークになることを参照してください。

また、内部で何が起こっているかについてのコンテキストについては、ASP.NET を使用した進化可能な Web API の設計の HttpClient の章、特にここで引用されている「ライフサイクル」セクションを読むことを強くお勧めします。

HttpClient は IDisposable インターフェイスを間接的に実装しますが、HttpClient の標準的な使用法は、すべての要求の後にそれを破棄することではありません。HttpClient オブジェクトは、アプリケーションが HTTP 要求を行う必要がある限り存続することを意図しています。オブジェクトが複数のリクエストにまたがって存在することで、DefaultRequestHeaders を設定する場所が有効になり、HttpWebRequest で必要だったすべてのリクエストで CredentialCache や CookieContainer などを再指定する必要がなくなります。

または、DotPeek を開くこともできます。

于 2013-03-29T17:49:38.927 に答える
19

私の理解では、呼び出しDispose()は、後で必要なリソースをロックしている場合にのみ必要です (特定の接続など)。使用していないリソースを保持するべきではないという理由だけで、使用していないリソースを解放することを常にお勧めします。

マイクロソフトの例は、必ずしも間違っているわけではありません。アプリケーションが終了すると、使用されたすべてのリソースが解放されます。HttpClientこの例の場合、これは is done が使用された直後に発生します。同様のケースでは、明示的に呼び出すことDispose()はやや不必要です。

しかし、一般に、クラスが を実装する場合、完全に準備ができてできるようになったらすぐにそのインスタンスを削除IDisposableする必要があると理解されています。これは、リソースまたは接続が保持されている/開いているかどうかについて明示的に文書化されていないDispose()ような場合に特に当てはまると思います。HttpClient接続が [すぐに] 再利用される場合は、それを放棄Dipose()することをお勧めします。その場合、「完全に準備ができている」わけではありません。

参照: IDisposable.Dispose メソッドおよびいつ Dispose を呼び出すか

于 2013-03-29T14:52:09.040 に答える
12

短い回答: いいえ、現在受け入れられている回答のステートメントは正確ではありません:「一般的なコンセンサスは、HttpClient を処分する必要はない (すべきではない) ということです」。

長い答え: 次のステートメントは両方とも真実であり、同時に達成可能です。

  1. 「HttpClient は、一度インスタンス化され、アプリケーションの存続期間を通じて再利用されることを意図しています」、公式ドキュメントから引用。
  2. オブジェクトは、破棄するIDisposableことが想定/推奨されています。

そして、それらは必ずしも互いに競合しません。コードをどのように整理して再利用し、HttpClientそれでも適切に破棄するかは問題です。

私の別の回答から引用されたさらに長い回答:

一部のブログ投稿HttpClientで、のIDisposableインターフェイスがパターンを使用する傾向があり、using (var client = new HttpClient()) {...}ソケット ハンドラーの枯渇の問題につながると非難する人々を見るのは偶然ではありません。

それは、暗黙の(誤解?)概念に帰着すると思います: 「IDisposableオブジェクトは短命であることが期待されています」

ただし、このスタイルでコードを書くと、確かに短命に見えますが:

using (var foo = new SomeDisposableObject())
{
    ...
}

IDisposableの公式ドキュメントには、IDisposableオブジェクトが短命でなければなら ないことについては言及されていません。定義上、IDisposable は、管理されていないリソースを解放できるようにするメカニズムにすぎません。これ以上何もない。その意味で、最終的に破棄をトリガーすることが期待されていますが、短期間で破棄する必要はありません。

したがって、実際のオブジェクトのライフサイクル要件に基づいて、廃棄をトリガーするタイミングを適切に選択するのはあなたの仕事です。IDisposable を長期間にわたって使用することを妨げるものは何もありません。

using System;
namespace HelloWorld
{
    class Hello
    {
        static void Main()
        {
            Console.WriteLine("Hello World!");

            using (var client = new HttpClient())
            {
                for (...) { ... }  // A really long loop

                // Or you may even somehow start a daemon here

            }

            // Keep the console window open in debug mode.
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }
    }
}

この新しい理解により、ブログ投稿を再検討すると、「修正」が一度初期化されるが決して破棄されないことが明確にわかります。HttpClientこれが、netstat 出力から、接続が ESTABLISHED 状態のままであることを確認できる理由です。適切に閉じられていません。クローズされている場合、その状態は代わりに TIME_WAIT になります。実際には、プログラム全体が終了した後に 1 つの接続のみが開いていることをリークすることは大した問題ではありません。ブログの投稿者は、修正後もパフォーマンスが向上していることを確認できます。それでも、IDisposable を非難して破棄しないことを選択するのは概念的に正しくありません。

于 2018-05-31T20:19:22.057 に答える
3

通常の使用法 (応答が 2GB 未満) では、HttpResponseMessages を破棄する必要はありません。

Stream Content が完全に読み取られていない場合、HttpClient メソッドの戻り値の型は Disposed にする必要があります。そうしないと、ストリームがガベージ コレクションされるまで、それらのストリームを閉じることができることを CLR が知る方法がありません。

  • データを byte[] (GetByteArrayAsync など) または文字列に読み込む場合は、すべてのデータが読み込まれるため、破棄する必要はありません。
  • 他のオーバーロードは、デフォルトで最大 2 GB のストリームを読み取ります (HttpCompletionOption は ResponseContentRead であり、HttpClient.MaxResponseContentBufferSize のデフォルトは 2 GB です)。

HttpCompletionOption を ResponseHeadersRead に設定するか、応答が 2GB を超える場合は、クリーンアップする必要があります。これは、HttpResponseMessage で Dispose を呼び出すか、HttpResonseMessage Content から取得した Stream で Dispose/Close を呼び出すか、コンテンツを完全に読み取ることによって実行できます。

HttpClient で Dispose を呼び出すかどうかは、保留中の要求をキャンセルするかどうかによって異なります。

于 2013-12-26T15:14:07.650 に答える
-2

HttpClient のインスタンスを作成して常に閉じる必要がないようにするには、シングルトン パターンを使用する必要があると思います。.Net 4.0 を使用している場合は、以下のサンプル コードを使用できます。シングルトン パターン チェックの詳細については、こちらを参照してください

class HttpClientSingletonWrapper : HttpClient
{
    private static readonly Lazy<HttpClientSingletonWrapper> Lazy= new Lazy<HttpClientSingletonWrapper>(()=>new HttpClientSingletonWrapper()); 

    public static HttpClientSingletonWrapper Instance {get { return Lazy.Value; }}

    private HttpClientSingletonWrapper()
    {
    }
}

以下のコードを使用します。

var client = HttpClientSingletonWrapper.Instance;
于 2013-10-04T12:37:54.087 に答える