10
votes

Pouvez-vous faire une impasse sur appeler gc.Collect et gc.waitforpendingNinalisateurs?

Compte tenu des éléments suivants:

GC.Collect(GC.MaxGeneration);
GC.WaitForPendingFinalizers();
GC.Collect(GC.MaxGeneration);


1 commentaires

Est-ce une question strictement préventive ou connaissez-vous une situation d'impasse? (Si vous dépannez, il serait utile de publier le code de votre (s) méthode (s) de finaliser).)


3 Réponses :


6
votes
// causes a deadlock when built with release config and no debugger attached
// building in debug mode and/or attaching the debugger might keep
// badIdea alive for longer, in which case you won't see the deadlock
// unless you explicitly set badIdea to null after calling Monitor.Enter

var badIdea = new BadIdea();
Monitor.Enter(badIdea);

GC.Collect(GC.MaxGeneration);
GC.WaitForPendingFinalizers();
GC.Collect(GC.MaxGeneration);

// ...

public class BadIdea
{
    ~BadIdea()
    {
        lock (this)
        {
            // ...
        }
    }
}

0 commentaires

4
votes

Il y a une histoire célèbre sur WaitForPendentinAinalisateurs, décrit par Jeffrey Richter. Il est montré ici: http://haacked.com/archive/2005/04/12/neverlockque.aspx

class MyClass
{

private bool isDisposed = false;

    ~MyClass()
    {
        lock(this)
        {
            if(!isDisposed)
            {
                // ...
            }
        }
    }
}
...
MyClass instance = new MyClass();

Monitor.Enter(instance);
instance = null;

GC.Collect();
GC.WaitForPendingFinalizers();


0 commentaires

4
votes

Vous n'écrirez aucune sorte de situation d'impasse lors de l'appelant gc.collect et gc.waitforpendendsFinalizers sauf si vous accédez à des objets gérés dans Votre finaliser les méthodes . Les méthodes d'appel d'autres objets avec une portée publique pourraient potentiellement conduire à des résultats imprévisibles, y compris une impasse. La raison en est que vous n'êtes pas au contrôle total de ces modèles de verrouillage des autres objets. Il pourrait être verrouillé par quiconque alors que votre méthode de finaliseur tente d'y accéder.

En outre, verrouillage Ceci explicitement dans le finaliseur est presque garanti pour provoquer une impasse, la réponse de Lukeh le démontre. Vous pouvez lire l'article original de Jeffrey Richter sur ce ici .

En général, vous ne devez émerger que des ressources non gérées dans vos finaliseurs, ce qui devrait atténuer de telles préoccupations concernant des blocages.


4 commentaires

En fait, il existe des situations où il peut être utile d'avoir des objets finalisables contenant des références à d'autres objets gérés. Par exemple, parce que bitmap est cher, un programme qui doit stocker des centaines d'icônes 16x16 pourrait définir une classe qui alloue des bitmaps de 16x1024-pixels, chacun pouvant contenir 64 icônes, puis renvoyer des objets chacun dont contient une référence à un allocator bitmap avec une indication de quelle partie elle "possède". Lorsqu'un tel objet "piece" est disposé, l'allocateur peut rendre cette partie du bitmap plus grand à la disposition de quelqu'un d'autre.


Même si les seules références fortes au plus grand bitmap étaient dans les objets «bitmap», de sorte que l'abandon de tous ces objets rendrait le plus grand bitmap éligible à la finalisation, abandonnant tout sauf l'un des objets «bitmap» associés à un bitmap serait laisser tout le temps alloué mais surtout indisponible à d'autres fins. L'ajout d'un finisseur à l'objet bitmap-pièce permettrait que l'objet maître de réutiliserait des pièces abandonnées. Bien entendu, ce code doit être écrit avec soin pour éviter les objets de blocage ou d'autres malheurs de filetage, mais il peut toujours être utile.


Je ne comprends pas ce que cela a à voir avec cette réponse ou cette question. @supercat


Je réagissais à votre dernière déclaration, cependant de la relecture, je remarque que cela dit "en général ..." Mon point était qu'il existe des situations où il peut être approprié pour les finaliseurs d'interagir avec les ressources gérées.