2017-04-10 4 views
1

Je souhaite effectuer une requête HTTP lorsqu'un objet est collecté par le récupérateur de place. Je mets un simple appel dans le finailzer de la classe, qui fonctionne bien tant que l'application ne s'arrête pas.Demande HttpClient dans le finaliseur dans .NET

Lorsque le programme est terminé et que mon application veut s'arrêter, le CPG appelle le finaliseur comme auparavant, mais cette fois la requête est bloquée ou quitte simplement sans exception. Au moins le Studio ne montre pas d'exception, le programme se termine juste quand l'appel est envoyé.

Malheureusement, je dois utiliser le finaliseur pour envoyer cette requête, donc ne suggère pas d'utiliser Dispose à la place du finaliseur. Trouvons juste un moyen de le faire à partir de là si possible. :)

Voici la partie importante de mon code:

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 
} 
+0

Comment savez-vous que cela ne fonctionne pas? Le code sortant sans exception n'est pas la preuve qu'il ne fonctionne pas. Voulez-vous faire un POST et inclure des données plutôt qu'un GET?Est-il possible que le finaliseur ne soit pas appelé pour l'objet? – Luke

+0

Bonne question. La demande ne s'affiche tout simplement pas de l'autre côté. Et le vrai code utilise POST, ce qui ne fait aucune différence avec cet exemple avec GET. –

+1

Vous ne pouvez pas faire cela lorsque l'exécution est terminée. Mais je suis curieux de savoir quelle situation pourrait conduire à une exigence de faire une demande web dans le finaliseur? – Evk

Répondre

0

Avez-vous essayé ex = null; avant GC.Collect();

Déposer une requête HTTP, et en général rien faire non trivial, à partir d'un finaliseur , est outrageusement mal conçu. S'attendre à ce que cela fonctionne même si votre application est en cours de fermeture, est au-delà de la mauvaise conception. À ce moment-là, une partie de la pile responsable de la livraison de votre requête HTTP a peut-être déjà été récupérée. Vous avez très peu de chances de le faire fonctionner. Votre seul espoir de ce jamais travailler est lors d'un appel à GC.WaitForPendingFinalizers()avant vos retours Main().

Cependant, vous essayez de faire des choses trop complexes à l'intérieur de votre finaliseur. Si vous google autour pour le modèle "élimination obligatoire", vous trouverez la recommandation que la seule chose qu'un finaliseur devrait jamais faire est de produire une entrée de journal des erreurs sur le fait que quelque programmeur, quelque part, a oublié d'invoquer Dispose(). Si vous insistez pour effectuer un travail réel lors de la finalisation, je vous recommande de réécrire votre destructeur pour ajouter vos "données importantes" à une file d'attente et laisser un autre objet traiter cette file d'attente. Bien sûr, ce traitement devra tous être fait avant le dernier } de Main(). Une fois que vous êtes passé le dernier } de Main(), "il y aura des dragons".

+0

Au moins maintenant je sais avec certitude que cela ne fonctionnera pas :) merci –

2

Vous rencontrez un mur ici. Finaliseurs ne sont pas garanties à exécuter dans toutes les conditions:

Are .net finalizers always executed?

Un finaliseur peut ne pas fonctionner, par exemple, si:

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 

C'est pourquoi il est déconseillé pour utiliser les finaliseurs en dehors de quelques cas, il est conçu pour: 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 
+0

Bien dans ce cas finaliseur est exécuté, mais il n'est pas exécuté à la fin – Evk