.NET 终结器中的 HttpClient 请求

HttpClient request in finalizer in .NET

我想在垃圾收集器收集对象时发出 HTTP 请求。我在 class 的终结器中进行了一个简单的调用,只要应用程序没有关闭,它就可以正常工作。

当程序结束,我的应用程序要关闭时,GC 像以前一样调用终结器,但这次请求卡住了,或者只是无异常退出。至少 Studio 没有显示异常,程序只是在发送调用时终止。

不幸的是我必须使用终结器来发送这个请求,所以请不要建议使用 Dispose 代替终结器。如果可能的话,让我们从那里找到一种方法。 :)

这是我的代码的重要部分:

class MyExample
{
    private readonly HttpClient myClient;

    public MyExample()
    {
        var handler = new HttpClientHandler();
        handler.UseProxy = false;
        handler.ServerCertificateCustomValidationCallback = (a, b, c, d) => true;

        this.myClient = new HttpClient(handler);
        this.myClient.BaseAddress = new Uri("https://wonderfulServerHere");
    }

    public async void SendImportantData() => await this.myClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, "ImportantData"));

    ~MyExample()
    {
        this.SendImportantData();
    }
}

class Program
{
    static void Main(string[] args)
    {
        MyExample ex = new MyExample();

        /* ... */

        ex = new MyExample();

        /* ... */

        GC.Collect();
        GC.WaitForPendingFinalizers(); // Works fine here

       /* ... */
    } // Doesn't work here
}

你在GC.Collect();

之前尝试过ex = null;

在终结器中发出 HTTP 请求,并通常做任何不重要的事情,是非常错误的构思。期望即使在您的应用程序关闭时它也能正常工作,这是非常不明智的。那时,负责传递 HTTP 请求的堆栈的一部分可能已经被垃圾收集。你很少有机会让它发挥作用。您唯一的希望是在 GC.WaitForPendingFinalizers() 您的 Main() returns 之前调用 GC.WaitForPendingFinalizers()

但是,您仍然试图在终结器中执行过于复杂的操作。如果你 google 围绕 "mandatory disposal" 模式,你会发现建议终结器唯一应该做的就是产生一个错误日志条目,说明某个程序员在某个地方忘记了调用 Dispose().

如果您坚持在完成后进行实际工作,我建议重写您的析构函数以将您的 "Important Data" 添加到队列中,并让其他对象处理该队列。当然,这个处理都需要在之前Main()的最后}完成。一旦你过了 Main() 的最后一个 },"there be dragons"。

你在这里碰壁了。 不保证在所有条件下都执行终结器:

Are .net finalizers always executed?

A finalizer may not run, for example, if:

Another finalizer throws an exception.
Another finalizer takes more than 2 seconds.
All finalizers together take more than 40 seconds.
An AppDomain crashes or is unloaded (though you can circumvent this with a critical finalizer (CriticalFinalizerObject, SafeHandle or something like that)
No garbage collection occurs
The process crashes

这就是为什么不建议使用终结器的原因,除了它设计用于以下情况的少数情况: https://csharp.2000things.com/tag/finalizer/

Implement a finalizer only when the object has unmanaged resources to clean up (e.g. file handles)
Do not implement a finalizer if you don’t have unmanaged resources to clean up
The finalizer should release all of the object’s unmanaged resources
Implement the finalizer as part of the dispose pattern, which allows for deterministic destruction
The finalizer should only concern itself with cleanup of objects owned within the class where it is defined
The finalizer should avoid side-effects and only include cleanup code
The finalizer should not add references to any objects, including a reference to the finalizer’s own object
The finalizer should not call methods in any other objects