Attendre la fin d'un événement écoulé avant que l'application / le service ne se ferme / s'arrête
Résumé:
Au sein d'un service Windows & Application de Console que j'appelle une bibliothèque commune qui contient un Timer qui déclenche régulièrement une action qui prend environ 30 secondes pour terminer. Cela fonctionne bien, mais...
Lorsqu'un arrêt ou la fermeture de l'application est appelée et que la minuterie est en ElapsedEventHandler j'ai besoin de l'arrêt du service/de l'application sortie d'attendre jusqu'à ce que le gestionnaire d'événement est terminé.
J'ai implémenté cette fonctionnalité en ayant un Booléen InEvent propriété qui est vérifiée lorsque la minuterie d'arrêt de la méthode est appelée.
Tout ce qui est fonctionnel, la question est: Est-ce la meilleure façon de faire cela? Est-il une autre approche qui peut servir à cette fin mieux?
L'autre problème est que j'ai besoin pour éviter le service de demande d'arrêt à défaut avec un "Service n'a pas répondu à la demande d'arrêt"
C'est ma mise en œuvre
public sealed class TimedProcess : IDisposable
{
static TimedProcess singletonInstance;
bool InEvent;
Timer processTimer;
private TimedProcess()
{
}
public static TimedProcess Instance
{
get
{
if (singletonInstance == null)
{
singletonInstance = new TimedProcess();
}
return singletonInstance;
}
}
public void Start(double interval)
{
this.processTimer = new Timer();
this.processTimer.AutoReset = false;
this.processTimer.Interval = interval;
this.processTimer.Elapsed += new ElapsedEventHandler(this.processTimer_Elapsed);
this.processTimer.Enabled = true;
}
public void Stop()
{
if (processTimer != null)
{
while (InEvent)
{
}
processTimer.Stop();
}
}
void processTimer_Elapsed(object sender, ElapsedEventArgs e)
{
try
{
InEvent = true;
//Do something here that takes ~30 seconds
}
catch
{
}
finally
{
InEvent = false;
processTimer.Enabled = true;
}
}
public void Dispose()
{
if (processTimer != null)
{
Stop();
processTimer.Dispose();
}
}
}
Et ce est la façon dont il est appelé dans le service de Démarrage /de la console principale de l'application:
TimedProcess.Instance.Start(1000);
C'est la façon dont il est appelé au service OnStop et principale de l'application (en attente d'une pression de touche):
TimedProcess.Instance.Stop();
source d'informationauteur MrEyes
Vous devez vous connecter pour publier un commentaire.
Probablement la méthode la plus simple et la plus fiable est d'utiliser un
Monitor
. Créer un objet que le programme principal et la minuterie de rappel: laVotre programme principal tente de verrouillage avant de s'éteindre:
Et votre minuterie de rappel de ce qu'il se verrouille, trop:
Le programme principal ne doit jamais relâcher le verrou une fois qu'il est obtenu.
Si vous souhaitez que le programme principal aller de l'avant et arrêter après une certaine période de temps, indépendamment de savoir si il est obtenu de la serrure, l'utilisation
Monitor.TryEnter
. Par exemple, cela permettra d'attendre 15 secondes.La valeur de retour est
true
si elle a été en mesure d'obtenir le verrou.Par le chemin, je suggère fortement que vous utilisez
System.Threading.Timer
plutôt queSystem.Timers.Timer
. Le dernier courges d'exceptions, ce qui peut cacher des bugs. Si une exception se produit dans votreElapsed
cas, il ne sera jamais s'échapper, ce qui signifie que vous n'avez jamais le savoir. Voir mon post de blog pour plus d'informations.MODIFIER
Chaque rappel à la
System.Timers.Timer
est en attente sur leThreadPool
. Sachez que leSystem.Timers.Timer
peut ont une condition de course (vous pouvez en lire plus à ce sujet ici.)System.Threading.Timer
est un peu plus joli emballage, je préfère utiliser en raison de sa simplicité d'utilisation.Vous n'avez pas décrit assez de détails pour savoir si votre application peut gérer que condition de course, de sorte qu'il est difficile à dire. Mais compte tenu de votre code, il est possible qu'il pourrait y avoir un rappel de file d'attente pour les
processTimer_Elapsed
aprèsStop()
est appelé.Pour le service de problème de délai d'attente --
Une façon de faire cela est de faire un appel à la
ServiceController
méthodeWaitForStatus
avec un délai d'attente. J'ai fait cela dans le passé et il fonctionne raisonnablement bien, bien que je me souviens de la présence de quelques cas de bord autour d'attente pour un temps très long.Voir le MSDN de référence. Un exemple d'utilisation est décrite ici.
Une alternative possible semble être de ne pas faire le travail réel dans la minuterie de rappel lui-même, mais seulement de la file d'attente d'un élément de travail à partir de là, sur la table de roulement de la piscine pour faire le travail. Ensuite, vous pouvez aller de l'avant et de disposer de la minuterie - tout ce qui est actuellement en cours d'exécution sur le pool de threads restera opérationnel, et de votre service peut répondre à la demande d'arrêt immédiatement, mais le pool de threads élément (si la file d'attente) seront toujours traitées.