CPU amical boucle infinie

écrire une boucle infinie est simple:

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

mais cela va détruire la performance du CPU. Ce fil d'exécution va prendre autant que possible de la puissance du CPU.

Quelle est la meilleure façon de réduire l'impact sur le CPU? Ajouter un peu de Thread.Sleep(n) devrait faire l'affaire, mais mettre une valeur de timeout élevée pour la méthode Sleep() peut indiquer une application sans réponse au système d'exploitation.

disons que je dois effectuez une tâche chaque minute environ dans une application de console. Je dois garder Main() en" boucle infinie " pendant qu'une minuterie déclenche l'événement qui fera le travail. Je voudrais garder Main() avec le plus faible impact sur CPU.

quelles méthodes suggérez-vous? Sleep() peut être correct, mais comme je l'ai déjà mentionné, cela pourrait indiquer un fil non-réactif au système d'exploitation.

ÉDITE PLUS TARD:

je veux mieux expliquer ce que je cherche:

  1. j'ai besoin d'une application de console pas service Windows. Les applications de Console peuvent simuler les Services Windows sur Windows Mobile 6.systèmes x avec cadre Compact.

  2. j'ai besoin d'un moyen pour garder l'application en vie aussi longtemps que L'appareil mobile de Windows est en cours d'exécution.

  3. nous savons tous que l'application de la console fonctionne aussi longtemps que sa fonction main () statique fonctionne, donc j'ai besoin d'un moyen d'empêcher la sortie de la fonction Main ().

  4. dans des situations spéciales (comme: mise à jour de l'application), je dois demander à l'application d'arrêter, donc je dois boucler et tester en boucle infinie pour certaines conditions de sortie. Par exemple, c'est pourquoi Console.ReadLine() n'est d'aucune utilité pour moi. Il n'y a pas de sortie contrôle de condition.

  5. en ce qui concerne ce qui précède, je veux toujours Main() fonction comme ressource conviviale possible. Laissez l'empreinte digitale de la fonction qui vérifie l'état de sortie.

50
demandé sur elimad 2011-09-13 16:50:05

11 réponses

pour éviter la boucle infinity il suffit d'utiliser un WaitHandle . Pour faire sortir le processus du monde extérieur, utilisez un EventWaitHandle avec une chaîne unique. Ci-dessous est un exemple.

si vous démarrez la première fois, il imprime un message toutes les 10 Secondes. Si vous commencez dans l'intervalle une deuxième instance du programme il informera l'autre processus de sortie gracieusement et se termine aussi immédiatement. L'utilisation CPU pour cette approche: 0%

private static void Main(string[] 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(string message)
{
    Console.WriteLine(DateTime.Now + ": " + message);
}

private static void OnTimerElapsed(object state)
{
    Log("Timer elapsed.");
}
48
répondu Oliver 2012-09-11 10:40:21

vous pouvez utiliser le système .Le filetage.Classe de minuterie qui fournit la capacité d'exécuter le rappel asynchrone dans une période donnée de temps.

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

comme alternative il y a le système .Minuterie.Minuterie classe qui expose événement écoulé qui se produit lorsqu'une période de temps donnée est écoulée.

9
répondu sll 2011-09-13 12:52:36

Pourquoi voulez-vous tolérer l'utilisation d'une boucle infinie? Dans cet exemple, l'établissement du programme comme tâche planifiée, à exécuter à chaque minute, ne serait-il pas plus économique?

1
répondu anothershrubery 2011-09-13 12:53:00

pourquoi ne pas écrire une petite application et utiliser le planificateur de tâches du système pour l'exécuter chaque minute, heure...etc?

une autre option serait d'écrire un service Windows qui tourne en arrière-plan. Le service pourrait utiliser une classe D'alarme simple comme la suivante 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. À l'intérieur, cette classe D'alarme utilise une minuterie:

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

règle juste l'intervalle du minuteur correctement (par exemple 60000 millisecondes) et il relèvera périodiquement l'événement écoulé. Attachez un gestionnaire d'événement à L'événement écoulé pour effectuer votre tâche. Pas besoin de mettre en place une "boucle infinie" juste pour garder l'application vivante. Ceci est géré par le service.

1
répondu Christophe Geers 2011-09-13 12:57:52

il me semble que vous voulez que Main() entre dans une boucle interruptable. Pour que cela se produise, plusieurs threads doivent être impliqués quelque part (ou votre boucle doit sonder 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 principale() qu'il devrait se terminer.

si c'est vrai, alors je pense que vous voulez utiliser un ManualResetEvent ou un Evenementwaithandle . Vous pouvez attendre que cet événement soit signalé (et la signalisation devra être faite par un autre thread).

par exemple:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace Demo
{
    class Program
    {
        static void Main(string[] 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");
    }
}
1
répondu Matthew Watson 2012-09-11 09:36:49

vous pouvez utiliser Begin-/End-Invoke pour céder à d'autres fils. Par exemple:

public static void ExecuteAsyncLoop(Func<bool> loopBody)
{
    loopBody.BeginInvoke(ExecuteAsyncLoop, loopBody);
}

private static void ExecuteAsyncLoop(IAsyncResult result)
{
    var func = ((Func<bool>)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.
    });

ceci utilisait 60% d'un noyau sur ma machine (boucle complètement vide). Alternativement, vous pouvez 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();
}

qui utilisait 20% d'un noyau sur ma machine.

0
répondu Jonathan Dickinson 2011-09-13 13:14:17

Pour exposer sur un commentaire CodeInChaos:

vous pouvez définir un priorité du fil . Les Threads sont programmés pour l'exécution en fonction de leur priorité. L'algorithme d'ordonnancement utilisé pour déterminer l'ordre d'exécution du fil varie avec chaque système d'exploitation. Tous les threads par défaut à la priorité "normale", mais si vous définissez votre boucle à faible; il ne devrait pas voler le temps des threads définis à la normale.

0
répondu Tremmors 2011-09-13 13:51:52

j'ai fait ceci pour une application qui a dû traiter des fichiers comme ils ont été abandonnés sur un dossier. Votre meilleur pari est une minuterie (comme suggéré) avec une Console.ReadLine () à la fin de "main" sans mettre de boucle.

maintenant, votre préoccupation de dire à l'application d'arrêter:

j'ai aussi fait cela via un moniteur rudimentaire de" fichier". Il suffit de créer le fichier "quit.txt" dans le dossier racine de l'application (par mon programme ou une autre application qui pourrait lui demander d'arrêter) fera cesser la demande. Demi-code:

<do your timer thing here>
watcher = new FileSystemWatcher();
watcher.Path = <path of your application or other known accessible path>;
watcher.Changed += new FileSystemEventHandler(OnNewFile);
Console.ReadLine();

le nouveau fichier 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, peut-être que vous avez juste besoin de "tuer" le processus (vous avez dit "dans des situations spéciales (comme: mise à jour de l'application), je dois demander à l'application d'arrêter". Qui que soit le" demandeur" arrête, elle est, il suffit de tuer le processus)

0
répondu Nelson Rodriguez 2012-05-18 20:01:31

l'approche de minuterie est probablement votre meilleur pari, mais puisque vous mentionnez le fil.Sleep there est un intéressant fil.SpinWait ou spinwait struct alternative pour des problèmes similaires qui peuvent parfois être mieux que le fil court.Invocations du sommeil.

Voir aussi cette question: Quel est le but du fil.La méthode SpinWait?

0
répondu Zaid Masud 2017-05-23 12:10:48

Beaucoup de "avancée" de réponses ici, mais de l'OMI simplement à l'aide d'un Fil.Le sommeil (de faible valeur) devrait suffire pour la plupart.

Les minuteries

sont aussi une solution, mais le code derrière une minuterie est aussi une boucle d'infini - je suppose - qui déclenche votre code sur les intervalles écoulés, mais ils ont la configuration correcte de boucle d'infini.

Si vous avez besoin d'un grand sommeil, vous pouvez le couper en petits dort.

Donc quelque chose comme ceci est un simple et facile Solution CPU 0% pour une application non-UI.

static void Main(string[] 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);
        }
    }
}

concernant la façon dont L'OS détecte si l'application ne répond pas. Je ne connais pas d'autres tests que sur les demandes D'assurance-chômage, où il y a des méthodes pour vérifier si le thread D'assurance-chômage traite le code D'assurance-chômage. Fil dort sur l'INTERFACE utilisateur pourra facilement être découvert. L'Application Windows "ne répond pas" utilise une méthode native simple "SendMessageTimeout" pour voir détecter si l'application a une interface utilisateur qui ne répond pas.

toute boucle infinity sur une application D'interface utilisateur doit toujours être exécutée dans un thread séparé.

0
répondu Wolf5 2016-05-20 10:33:06

pour que les applications de la console continuent à fonctionner, il suffit d'ajouter un Console.ReadLine() à la fin de votre code dans Main() .

si l'utilisateur ne devrait pas pouvoir terminer l'application, vous pouvez le faire avec une boucle comme celle-ci:

while (true){
   Console.ReadLine();
}
-1
répondu Peter 2011-09-13 13:00:09