Question

J'ai une classe nommée BackgroundWorker dans laquelle un thread est en cours d'exécution. Pour désactiver ce thread, une variable d'instance nommée stop doit être true .

Pour m'assurer que le fil est libéré lorsque la classe est utilisée, j'ai ajouté IDisposable et un finaliseur qui appelle Dispose () . En supposant que stop = true provoque effectivement la fermeture de ce thread, ce sippet est-il correct? C'est bien d'appeler Dispose depuis un finaliseur, non?

Les finalistes doivent toujours appeler Dispose si l'objet hérite de IDisposable , non?

/// <summary>
/// Force the background thread to exit.
/// </summary>
public void Dispose()
{
    lock (this.locker)
    {
        this.stop = true;
    }
}

~BackgroundWorker()
{
    this.Dispose();
}
Était-ce utile?

La solution

Votre code est correct, bien que verrouiller un finaliseur soit quelque peu "effrayant". et je l'éviterais - si vous vous retrouviez dans une impasse ... Je ne suis pas tout à fait certain de ce qui se passerait, mais ce ne serait pas bien. Cependant, si vous êtes en sécurité, cela ne devrait pas être un problème. La plupart. Les éléments internes de la collecte des ordures sont douloureux et j’espère que vous n’aurez jamais à les voir;)

Comme le souligne Marc Gravell, une booléenne volatile vous permettrait de vous débarrasser du verrou, ce qui atténuerait ce problème. Appliquez cette modification si vous le pouvez.

Le code de

nedruod place la tâche dans le contrôle if (disposition), ce qui est complètement faux - le fil est une ressource non gérée et doit être arrêté même si elle n'est pas explicitement supprimée. Votre code est correct, je signale simplement que vous ne devez pas suivre les conseils donnés dans cet extrait de code.

Oui, vous devez presque toujours appeler Dispose () depuis le finaliseur si vous implémentez le modèle IDisposable. Le motif IDisposable complet est un peu plus grand que ce que vous avez, mais vous n’en avez pas toujours besoin - il vous offre simplement deux possibilités supplémentaires:

  1. détecter si Dispose () a été appelé ou si le finaliseur est en cours d'exécution (vous n'êtes pas autorisé à toucher aux ressources gérées du finaliseur, en dehors de l'objet en cours de finalisation);
  2. permettant aux sous-classes de remplacer la méthode Dispose ().

Autres conseils

Tout d’abord, un avertissement grave . N'utilisez pas de finaliseur comme vous. Vous vous préparez pour de très mauvais effets si vous prenez des verrous dans un finaliseur. Petite histoire est de ne pas le faire. Passons maintenant à la question initiale.

public void Dispose()
{
    Dispose(true);
    GC.SuppressFinalize(this);
}

/// <summary>
/// Force the background thread to exit.
/// </summary>
protected virtual void Dispose(bool disposing)
{
    if (disposing)
    {
        lock (this.locker)
        {
            this.stop = true;
        }
    }
}

~BackgroundWorker()
{
    Dispose(false);
}

La seule raison d'avoir un finaliseur est de permettre aux sous-classes d'étendre et de libérer des ressources non gérées . Si vous n’avez pas de sous-classes, fermez votre classe et lâchez complètement le finaliseur.

Par intérêt, quelle que soit la raison pour laquelle cela ne pourrait pas utiliser le fichier BackgroundWorker , qui prend totalement en charge l'annulation?

En ce qui concerne le verrouillage - un champ booléen volatil pourrait être moins gênant.

Cependant, dans ce cas, votre finaliseur ne fait rien d’intéressant, surtout compte tenu du paramètre "if (disposition)". - c’est-à-dire que le code intéressant n’exécute que pendant Dispose (). Personnellement, je serais tenté de rester juste avec IDisposable et de ne pas fournir de finaliseur: vous devriez le nettoyer avec Dispose ().

Est-ce que le " stop " instance variable une propriété? Sinon, il est inutile de le définir lors du finaliseur: plus rien ne fait référence à l'objet, donc rien ne peut interroger le membre.

Si vous publiez une ressource, laisser Dispose () et le finaliseur effectuer le même travail (tout d’abord, tester si le travail doit encore être fait) est un bon modèle.

Vous avez besoin du motif jetable complet, mais le thread doit pouvoir y accéder. S'il s'agit d'une variable membre de la classe en cours d'élimination, ce n'est pas bon, car elle ne peut pas faire référence à une classe supprimée. Pensez à avoir un événement que le thread possède et à signaler à sa place.

L'objet qui implémente le finaliseur a besoin d'une référence à un indicateur - stocké dans un autre objet - que le thread pourra voir; le thread ne doit pas avoir de référence forte, directe ou indirecte, à l'objet qui implémente le finaliseur. Le finaliseur doit définir l'indicateur en utilisant quelque chose comme un CompareExchange et le thread doit utiliser un moyen similaire pour le tester. Notez que si le finaliseur d'un objet accède à un autre objet, l'autre objet peut avoir été finalisé mais il existera toujours. C'est bien pour un finaliseur de référencer d'autres objets s'il le fait d'une manière qui ne sera pas gênée par leur finalisation. Si tout ce que vous faites est de mettre un drapeau, tout va bien.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top