我参加聚会很晚,但这是我关于这个棘手主题的学习旅程。
1.在哪里可以找到重用HttpClient的官方倡导者?
我的意思是,如果打算重用HttpClient
且这样做很重要,那么可以更好地在自己的API文档中记录这样的拥护者,而不是隐藏在许多“高级主题”,“性能(反)模式”或其他博客文章中。否则,新学习者应该如何在为时已晚之前知道它?
截至目前(2018年5月),在谷歌搜索“ c#httpclient”时的第一个搜索结果指向MSDN上的该API参考页面,根本没有提及该意图。好吧,这里的新手课程1是,总是在MSDN帮助页面标题之后立即单击“其他版本”链接,您可能会在那里找到指向“当前版本”的链接。在这种HttpClient情况下,它将带您到此处包含该意图描述的最新文档
。
我怀疑许多不熟悉该主题的开发人员也没有找到正确的文档页面,这就是为什么这种知识没有广泛传播的原因,人们后来发现它们时可能会感到很困难,这使他们感到惊讶
。
2.的(误解)概念 using
IDisposable
这一个是稍微偏离主题,但仍值得指出的是,这不是巧合看到人们在前述的那些博客文章指责怎样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。实际上,在整个程序结束后只泄漏一个打开的连接并没什么大不了的,并且在修复后,博客发布者仍然可以看到性能的提高;但是,将IDisposable归咎于并选择不处置它在概念上是不正确的。
3.我们是否必须将HttpClient放入静态属性,或者甚至将其作为单例?
根据上一节的理解,我认为这里的答案很明确:“不一定”。只要您重用HttpClient并最终将其理想地处置,这实际上取决于您如何组织代码。
可笑的是,即使是当前官方文档的“ 备注”部分中的示例
也不完全正确。它定义了一个“ GoodController”类,其中包含一个不会被丢弃的静态HttpClient属性。这违反了“示例”部分中另一个示例所
强调的内容:“需要调用dispose ...这样应用程序才不会泄漏资源”。
最后,单身汉并非没有自己的挑战。
“有多少人认为全局变量是个好主意?没人。
有多少人认为单例是个好主意?一些。
是什么赋予了?单例只是一堆全局变量。”
-从这个鼓舞人心的演讲中引用“全球状态和单身人士”
PS:SqlConnection
这与当前的问答无关,但可能是一个好知识。SqlConnection的使用模式不同。您不需要重用SqlConnection,因为它将以这种方式更好地处理其连接池。
差异是由它们的实现方法引起的。每个HttpClient实例都使用其自己的连接池(从此处引用
)。但SqlConnection的本身是由一个中央连接池按照管理,这个。
而且,您仍然需要像对待HttpClient一样处理SqlConnection。