Boucle infinie compatible avec le processeur

Ecrire une boucle infinie est simple:

while(true){ //add whatever break condition here } 

Mais cela va détruire les performances du processeur. Ce thread d’exécution prendra autant que possible de la puissance du processeur.

Quel est le meilleur moyen de réduire l’impact sur le processeur? L’ajout de Thread.Sleep(n) devrait faire l’affaire, mais définir une valeur de délai d’expiration élevée pour la méthode Sleep() peut indiquer une application qui ne répond plus au système d’exploitation.

Disons que je dois effectuer une tâche à peu près toutes les minutes dans une application console. Je dois garder Main() cours d’exécution dans une “boucle infinie” tandis qu’une timer déclenche l’événement qui fera l’affaire. Je voudrais garder Main() avec le plus faible impact sur le CPU.

Quelles méthodes proposez-vous? Sleep() peut être correct, mais comme je l’ai déjà mentionné, cela peut indiquer un thread qui ne répond pas au système d’exploitation.

ÉDITION ULTÉRIEURE:

Je veux mieux expliquer ce que je recherche:

  1. J’ai besoin d’une application console et non d’un service Windows. Les applications de console peuvent simuler les services Windows sur les systèmes Windows Mobile 6.x avec Compact Framework.

  2. J’ai besoin d’un moyen de maintenir l’application en vie tant que l’appareil Windows Mobile est en cours d’exécution.

  3. Nous soaps tous que l’application de console s’exécute tant que sa fonction statique Main () s’exécute, donc j’ai besoin d’un moyen d’empêcher la fonction Main () de quitter.

  4. Dans des situations spéciales (comme la mise à jour de l’application), je dois demander à l’application d’arrêter, donc je dois boucler et tester à l’infini certaines conditions de sortie. Par exemple, c’est pourquoi Console.ReadLine() n’est pas utile pour moi. Il n’y a pas de vérification de la condition de sortie.

  5. En ce qui concerne ce qui précède, je souhaite toujours que la fonction Main () soit aussi conviviale que possible. Laissez l’empreinte de la fonction qui vérifie la condition de sortie.

Pour éviter la boucle infini, utilisez simplement un WaitHandle . Pour laisser le processus sortir du monde extérieur, utilisez un EventWaitHandle avec une chaîne unique. Voici un exemple.

Si vous le lancez la première fois, il imprime simplement un message toutes les 10 secondes. Si, dans l’intervalle, vous lancez une seconde instance du programme, il informera l’autre processus de sa sortie normale et se quittera aussi immédiatement. L’utilisation du processeur pour cette approche: 0%

 private static void Main(ssortingng[] args) { // Create a IPC wait handle with a unique identifier. bool createdNew; var waitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, "CF2D4313-33DE-489D-9721-6AFF69841DEA", out createdNew); var signaled = false; // If the handle was already there, inform the other process to exit itself. // Afterwards we'll also die. if (!createdNew) { Log("Inform other process to stop."); waitHandle.Set(); Log("Informer exited."); return; } // Start a another thread that does something every 10 seconds. var timer = new Timer(OnTimerElapsed, null, TimeSpan.Zero, TimeSpan.FromSeconds(10)); // Wait if someone tells us to die or do every five seconds something else. do { signaled = waitHandle.WaitOne(TimeSpan.FromSeconds(5)); // ToDo: Something else if desired. } while (!signaled); // The above loop with an interceptor could also be replaced by an endless waiter //waitHandle.WaitOne(); Log("Got signal to kill myself."); } private static void Log(ssortingng message) { Console.WriteLine(DateTime.Now + ": " + message); } private static void OnTimerElapsed(object state) { Log("Timer elapsed."); } 

Vous pouvez utiliser la classe System.Threading.Timer qui permet d’exécuter un rappel de manière asynchrone sur une période donnée.

 public Timer( TimerCallback callback, Object state, int dueTime, int period ) 

Comme alternative, il y a la classe System.Timers.Timer qui expose l’ événement Elapsed qui se déclenche lorsqu’une période de temps donnée est écasting.

Pourquoi voudriez-vous tolérer l’utilisation d’une boucle infinie? Pour cet exemple, la configuration du programme en tant que tâche planifiée, à exécuter toutes les minutes, ne serait-elle pas plus économique?

Pourquoi ne pas écrire une petite application et utiliser le planificateur de tâches du système pour l’exécuter toutes les minutes, toutes les heures, etc.?

Une autre option serait d’écrire un service Windows qui s’exécute en arrière-plan. Le service peut utiliser une classe d’alarme simple comme celle-ci sur MSDN:

http://msdn.microsoft.com/en-us/library/wkzf914z%28v=VS.90%29.aspx#Y2400

Vous pouvez l’utiliser pour déclencher périodiquement votre méthode. En interne, cette classe d’alarme utilise un minuteur:

http://msdn.microsoft.com/en-us/library/system.timers.timer.aspx

Il suffit de définir correctement l’intervalle du temporisateur (par exemple 60000 millisecondes) et l’événement Elapsed sera déclenché périodiquement. Attachez un gestionnaire d’événements à l’événement Elapsed pour effectuer votre tâche. Pas besoin d’implémenter une “boucle infinie” juste pour garder l’application vivante. Ceci est géré pour vous par le service.

Il me semble que vous voulez que Main () entre dans une boucle interruptible. Pour que cela se produise, plusieurs threads doivent être impliqués quelque part (ou votre boucle doit interroger périodiquement; je ne discute pas de cette solution ici cependant). Soit un autre thread dans la même application, soit un thread dans un autre processus, doit être capable de signaler à votre boucle Main () qu’il doit se terminer.

Si cela est vrai, je pense que vous voulez utiliser un ManualResetEvent ou un EventWaitHandle . Vous pouvez attendre cet événement jusqu’à ce qu’il soit signalé (et la signalisation devra être effectuée par un autre thread).

Par exemple:

 using System; using System.Threading; using System.Threading.Tasks; namespace Demo { class Program { static void Main(ssortingng[] args) { startThreadThatSignalsTerminatorAfterSomeTime(); Console.WriteLine("Waiting for terminator to be signalled."); waitForTerminatorToBeSignalled(); Console.WriteLine("Finished waiting."); Console.ReadLine(); } private static void waitForTerminatorToBeSignalled() { _terminator.WaitOne(); // Waits forever, but you can specify a timeout if needed. } private static void startThreadThatSignalsTerminatorAfterSomeTime() { // Instead of this thread signalling the event, a thread in a completely // different process could do so. Task.Factory.StartNew(() => { Thread.Sleep(5000); _terminator.Set(); }); } // I'm using an EventWaitHandle rather than a ManualResetEvent because that can be named and therefore // used by threads in a different process. For intra-process use you can use a ManualResetEvent, which // uses slightly fewer resources and so may be a better choice. static readonly EventWaitHandle _terminator = new EventWaitHandle(false, EventResetMode.ManualReset, "MyEventName"); } } 

Vous pouvez utiliser Begin-/End-Invoke pour céder à d’autres threads. Par exemple

 public static void ExecuteAsyncLoop(Func loopBody) { loopBody.BeginInvoke(ExecuteAsyncLoop, loopBody); } private static void ExecuteAsyncLoop(IAsyncResult result) { var func = ((Func)result.AsyncState); try { if (!func.EndInvoke(result)) return; } catch { // Do something with exception. return; } func.BeginInvoke(ExecuteAsyncLoop, func); } 

Vous l’utiliseriez comme tel:

 ExecuteAsyncLoop(() => { // Do something. return true; // Loop indefinitely. }); 

Cela a utilisé 60% d’un kernel sur ma machine (boucle complètement vide). Vous pouvez également utiliser ce code ( source ) dans le corps de votre boucle:

 private static readonly bool IsSingleCpuMachine = (Environment.ProcessorCount == 1); [DllImport("kernel32", ExactSpelling = true)] private static extern void SwitchToThread(); private static void StallThread() { // On a single-CPU system, spinning does no good if (IsSingleCpuMachine) SwitchToThread(); // Multi-CPU system might be hyper-threaded, let other thread run else Thread.SpinWait(1); } while (true) { // Do something. StallThread(); } 

Cela a utilisé 20% d’un kernel sur ma machine.

Expliquer un commentaire réalisé par CodeInChaos:

Vous pouvez définir la priorité d’ un thread donné. Les threads doivent être exécutés en fonction de leur priorité. L’algorithme de planification utilisé pour déterminer l’ordre d’exécution des threads varie en fonction de chaque système d’exploitation. Tous les threads ont une priorité “normale”, mais si vous définissez votre boucle sur une valeur basse; il ne devrait pas voler le temps des threads mis à la normale.

Je l’ai fait pour une application qui devait traiter des fichiers à mesure qu’ils étaient déposés sur un dossier. Votre meilleur pari est une timer (comme suggéré) avec une Console.ReadLine () à la fin de “main” sans mettre en boucle.

Maintenant, votre souci de dire à l’application d’arrêter:

Je l’ai également fait via un moniteur rudimentaire “fichier”. La simple création du fichier “quit.txt” dans le dossier racine de l’application (par mon programme ou par une autre application susceptible de lui demander de s’arrêter) entraînera la fermeture de l’application. Semi-code:

  watcher = new FileSystemWatcher(); watcher.Path = ; watcher.Changed += new FileSystemEventHandler(OnNewFile); Console.ReadLine(); 

Le OnNewFile pourrait être quelque chose comme ceci:

 private static void OnNewFile(object source, FileSystemEventArgs e) { if(System.IO.Path.GetFileName(e.FullPath)).ToLower()=="quit.txt") ... remove current quit.txt Environment.Exit(1); } 

Maintenant, vous avez mentionné que c’est (ou pourrait être) pour une application mobile? Vous n’avez peut-être pas l’observateur du système de fichiers. Dans ce cas, il vous suffit peut-être de “tuer” le processus (vous avez dit “Dans des situations spéciales (comme: mettre à jour l’application), je dois demander à l’application de s’arrêter”. tuez simplement le processus)

L’approche Timer est probablement votre meilleure option, mais puisque vous mentionnez Thread.Sleep, il existe une alternative intéressante à la structure Thread.SpinWait ou SpinWait pour des problèmes similaires pouvant parfois être meilleurs que les invocations Thread.Sleep courtes.

Voir aussi cette question: Quel est le but de la méthode Thread.SpinWait?

Beaucoup de réponses “avancées” ici mais IMO utilisent simplement un Thread.Sleep (lowvalue) devrait suffire pour la plupart.

Les timers sont également une solution, mais le code derrière une timer est aussi une boucle à l’infini – je suppose que cela déclenche votre code sur les intervalles écoulés, mais la configuration de la boucle infinie est correcte.

Si vous avez besoin d’un grand sumil, vous pouvez le réduire en plus petits.

Donc, quelque chose comme ceci est une solution simple et facile de 0% de processeur pour une application non-interface utilisateur.

 static void Main(ssortingng[] args) { bool wait = true; int sleepLen = 1 * 60 * 1000; // 1 minute while (wait) { //... your code var sleepCount = sleepLen / 100; for (int i = 0; i < sleepCount; i++) { Thread.Sleep(100); } } } 

En ce qui concerne la façon dont le système d'exploitation détecte si l'application ne répond pas. Je ne connais aucun autre test que sur les applications d'interface utilisateur, où il existe des méthodes pour vérifier si le thread d'interface utilisateur traite le code de l'interface utilisateur. Thread dort sur l'interface utilisateur sera facilement découvert. L'application Windows "ne répond pas" utilise une méthode native simple "SendMessageTimeout" pour voir si l'application a une interface utilisateur sans réponse.

Toute boucle d'infini sur une application d'interface utilisateur doit toujours être exécutée dans un thread séparé.

Pour que les applications de la console continuent de fonctionner, ajoutez simplement Console.ReadLine() à la fin de votre code dans Main() .

Si l’utilisateur ne doit pas pouvoir terminer l’application, vous pouvez le faire avec une boucle comme celle-ci:

 while (true){ Console.ReadLine(); }