10
votes

Timètre C # non réentrant

J'essaie d'appeler une méthode f () chaque t heure, mais si l'invocation précédente de f () n'a pas Terminé encore, attendez que ce soit fini.

J'ai lu un peu sur les minuteries disponibles, mais je n'ai pas trouvé de bonne façon de faire ce que je veux, économiser pour l'écriture manuelle tout. Toute aide sur la manière de réaliser cela sera appréciée, même si je crains de ne pas pouvoir trouver une solution simple à l'aide de minuteries.

à clarifier, si t est une seconde, et f () exécute les durées arbitraires que j'ai écrites ci-dessous, alors: xxx

Notez que la nature de cette minuterie est que f () ne doit pas nécessairement être sûr en ce qui concerne l'entrée de la répétition et un bassin de tailles de taille 1 suffit ici.


2 commentaires

Le lien est mort.


@rastroWalker merci! Enlevé pour le moment, car ce n'est pas vraiment nécessaire ici.


8 Réponses :


8
votes

Vous pouvez simplement utiliser un niveau de niveau «global» (ou plus probable, une propriété publique dans la même classe que f () code>) qui retourne true si f () code > est déjà en cours d'exécution.

donc si f () code> était dans une classe nommée TimeDevent code>, la première chose f () code> Définir exécuté code> true p>

de cette façon de tirer chaque seconde, puis lance l'événement chronométré s'il ne manque pas déjà de

if (! chronomévent.running) chronomevent.f () code> p>

Vous avez commenté que f () code> ne répétera pas immédiatement s'il a fallu plus longtemps que l'intervalle de minuterie . C'est un point juste. J'inclurais probablement la logique comme celle-là intérieure f () code> de sorte que exécuter code> reste vrai. Donc, cela ressemblerait à ceci: P>

public void f(int t) // t is interval in seconds
{
   this.running = true;

   Stopwatch stopWatch = new Stopwatch();
   stopWatch.Start();

   do
   {
       stopwatch.Reset();

       // Do work here

   } while (stopWatch.Elapsed.Seconds > t); // repeat if f() took longer than t

   this.running = false;   
}


8 commentaires

Si vous faites cela, méfiez-vous des conditions de course.


Vrai, mais le questionneur a déclaré qu'il n'a pas besoin d'être multi-thread en toute sécurité


Très élégant, bien que cela signifie un 2e f ne suivra pas immédiatement un 10.5 seconde f , n'est-ce pas? Cela attendra 0,5 seconde entre eux ...


@Paulg Le questionneur a déclaré qu'il ne veut pas passer de temps à faire du temps F thread-coffre-fort. Quel interrogateur signifiait par f correspond à la partie "// fonctionne ici" partie de votre programme au lieu du f dans votre programme. Vous devez vous assurer que votre f fonctionne correctement même si le questionnaire f n'est pas un fil-coffre-fort. Cependant, le fait est que votre programme lui-même n'est pas thread-coffre-fort, même si le f est le fil-coffre-fort.


@Paulg Même définir exécutant volatile ne fera pas votre code correct. Considérons la possibilité que votre f soit préempté juste avant this.running = true est exécuté.


@Gène. Vous pouvez améliorer la sécurité du thread en vérifiant «si (this.running) quitte» en haut de F (à l'intérieur d'une serrure s'il existe d'autres accessoires d'écriture à «exécuter»). Merci d'avoir attiré cela à mon attention avec votre bowvote. Comme dit plus tôt, l'ajout de la sécurité du thread n'était pas l'accent sur la question ou la réponse.


@Paulg non, ce n'est pas sûr. Voir mon commentaire précédent. Eh bien, je n'ai pas expliqué assez clair. Si vous le vérifiez simplement, il n'est absolument pas sûr, car la mémoire différente de la mémoire peut être différente en raison de registres et de cache. Si vous le définissez volatile, cela forcera une synchronisation à chaque fois que la variable est lue. Cela semble résoudre le problème, mais envisager la possibilité que vous cotrageez à droite après avoir vérifié exécutant mais avant de définir exécuter à true. Vous avez besoin d'un verrou supplémentaire pour vous assurer que cela ne se produira pas.


@Paulg concernant 'Comme dit plus tôt, l'ajout de la sécurité du fil n'était pas l'accent sur la question ou la réponse ", je crois que j'ai expliqué mon raisonnement assez clair dans un commentaire précédent. L'OP ne veut pas f () réentrant et de sécurité, vous devez donc garantir f () ne sera pas appelé deux fois. Et c'est pourquoi vous devez ajouter de la sécurité thread-sécurité à votre code.



3
votes

Vous pouvez utiliser une minuterie non redémarrante, puis redémarrez manuellement la minuterie après la fin de la méthode.

Notez que cela entraînera du timing qui est quelque peu différent de ce que vous demandez. (Il y aura toujours un vide de T entre les invocations)

Vous pouvez résoudre celui-ci en réglant l'intervalle sur lasttick + t - maintenant et exécutant la méthode immédiatement si c'est <= 0 . .

méfiez-vous des conditions de race si vous devez arrêter la minuterie.


0 commentaires

1
votes

Vous ne pouvez pas obtenir une minuterie pour vous appeler exactement à intervalles réguliers. Toutes les minuteries font est de vous rappeler pas plus tôt que l'heure demandée.

Certaines minuteries sont meilleures que d'autres (par exemple, Windows.Forms.Timer est très irrégulière et peu fiable par rapport à System.threading.Timer)

Pour arrêter votre minuterie appelée à nouveau, une approche consiste à arrêter la minuterie pendant l'exécution de votre méthode. (Selon le type de minuterie que vous utilisez, arrêtez-le et recommencez à nouveau lorsque votre gestionnaire se déroule, ou avec quelques minuteries, vous pouvez demander un seul rappel plutôt que de répéter des rappels, chaque exécution de votre gestionnaire en aqueuse simplement l'appel suivant) .

Pour conserver le timing de manière relativement entre ces appels, vous pouvez enregistrer l'heure depuis la dernière exécution de votre gestionnaire et l'utiliser pour calculer le délai jusqu'à ce que le prochain événement soit requis. par exemple. Si vous souhaitez être appelé une fois par seconde et que votre minuterie a été remplie de providence à 1.02s, vous pouvez configurer le prochain rappel de la minuterie à une durée de 0,98s pour accueillir le fait que vous avez déjà "utilisé" une partie de la suivante. Deuxièmement lors de votre traitement.


0 commentaires

12
votes

Utilisez un système.Threading.Timer. Initialisez-le avec une période de délai d'attente.infinite afin qu'il agit comme une minuterie unique. Lorsque F () complète, appelez sa méthode Modifier () pour la recharger à nouveau.


1 commentaires

Avez-vous voulu dire la période définie sur timeout.infinite ?



0
votes

une solution simple: xxx


0 commentaires

0
votes

Que diriez-vous d'utiliser des délégués à la méthode F (), de la file d'attente à une pile et d'éclairer la pile à mesure que chaque délégué se termine? Vous avez toujours besoin de la minuterie, bien sûr.


0 commentaires

0
votes

Un fil simple est le moyen le plus simple d'y parvenir. Vous n'allez toujours pas être certain que votre appelé «précisément» quand vous le souhaitez, mais il devrait être proche ... vous pouvez également décider si vous souhaitez sauter des appels qui devraient se produire ou tenter d'attraper Sauvegarder ... Voici une routine d'assistance simple pour créer le fil. xxx


0 commentaires

0
votes

Pour le bénéfice des personnes qui atterrissent ici à la recherche de "retrénétrie": (Je sais que cela peut être trop tard pour la question initiale) Si l'on n'ait pas opposé à l'utilisation de bibliothèques open source qui prévoient déjà une telle fonctionnalité, je les ai réussies avec succès via une implémentation à l'aide de Quartz. NET Lorsque vous créez un travail et joignez un déclencheur, vous pouvez spécifier ce qui devrait être effectué si un déclencheur précédent n'a pas terminé l'exécution de son travail


0 commentaires