web-dev-qa-db-fra.com

Comment puis-je limiter par programme l'utilisation du processeur de mon programme à moins de 70%?

Dernièrement, je deviens plus axé sur la santé lors de la construction de mon programme, j'ai observé que la plupart des programmes prennent 2 ou 3 minutes à exécuter et lorsque je vérifie le planificateur de tâches, je vois qu'ils consomment 100% de l'utilisation du processeur, peut -Je limiter cette utilisation par programmation dans le code? Cela me permettra certainement d'exécuter plusieurs programmes à la fois.

Merci, Nidhi

45
Nidhi

Ce n'est pas votre souci ... C'est le travail du système d'exploitation de répartir le temps processeur entre les processus en cours d'exécution. Si vous souhaitez donner la priorité à d'autres processus pour accomplir leurs tâches, réduisez simplement la priorité de votre propre processus en modifiant Process.PriorityClass valeur pour cela.

Voir aussi: équivalent Windows de ‘Nice’

29
Shog9

Ce fil a plus de quatre ans et cela m'ennuie toujours que la réponse acceptée critique la question plutôt que d'y répondre. Il existe de nombreuses raisons valables pour lesquelles vous voudriez limiter le temps CPU pris par votre programme, je peux en énumérer quelques-unes du haut de ma tête.

Cela peut sembler un gaspillage de ne pas utiliser tous les cycles CPU gratuits disponibles, mais cette mentalité est imparfaite. Contrairement aux processeurs plus anciens, la plupart des processeurs modernes ne fonctionnent pas à une vitesse d'horloge fixe - beaucoup ont des modes d'économie d'énergie où ils baisse la vitesse d'horloge et la tension du processeur lorsque la charge est faible. Les processeurs consomment également plus d'énergie lors des calculs que lorsqu'ils exécutent des NOOP. Cela est particulièrement pertinent pour les ordinateurs portables qui nécessitent des ventilateurs pour refroidir le processeur lorsqu'il est sous une charge élevée. L'exécution d'une tâche à 100% pendant une courte période peut consommer beaucoup plus d'énergie que l'exécution d'une tâche à 25% pendant quatre fois plus longtemps.

Imaginez que vous écrivez une tâche en arrière-plan conçue pour indexer périodiquement des fichiers en arrière-plan. La tâche d'indexation doit-elle utiliser autant de CPU que possible à une priorité inférieure, ou se limiter à 25% et prendre autant de temps qu'il le faut? Eh bien, s'il devait consommer 100% du CPU sur un ordinateur portable, le CPU chaufferait, les ventilateurs se déclencheraient, et la batterie se déchargerait assez rapidement, et l'utilisateur serait ennuyé. Si le service d'indexation s'est étranglé, l'ordinateur portable peut fonctionner avec un refroidissement complètement passif à une vitesse et une tension d'horloge CPU très faibles.

Soit dit en passant, le service d'indexation de Windows se restreint désormais dans les nouvelles versions de Windows, ce qu'il n'a jamais fait dans les anciennes versions. Pour un exemple de service qui ne se résorbe toujours pas et qui agace fréquemment les gens, voir Module Windows Installer.

Un exemple de la façon de limiter une partie de votre application en interne en C #:

public void ThrottledLoop(Action action, int cpuPercentageLimit) {
    Stopwatch stopwatch = new Stopwatch();

    while(true) {
        stopwatch.Reset();
        stopwatch.Start();

        long actionStart = stopwatch.ElapsedTicks;
        action.Invoke();
        long actionEnd = stopwatch.ElapsedTicks;
        long actionDuration = actionEnd - actionStart;

        long relativeWaitTime = (int)(
            (1/(double)cpuPercentageLimit) * actionDuration);

        Thread.Sleep((int)((relativeWaitTime / (double)Stopwatch.Frequency) * 1000));
    }
}
87
Ryan

Tout d'abord, je suis d'accord avec Ryan que la question est parfaitement valable et il y a des cas où les priorités de threads ne sont pas du tout suffisantes. Les autres réponses semblent très théoriques et sans utilité pratique dans des situations où l'application est correctement conçue mais doit encore être limitée. Ryan offre une solution simple pour les cas dans lesquels une tâche relativement courte est effectuée à haute fréquence. Il y a des cas, cependant, où la tâche prend un très très long (disons une minute environ) et que vous ne pouvez pas ou ne voulez pas l'interrompre en petits morceaux entre lesquels vous pouvez faire la limitation. Dans ces cas, la solution suivante peut être utile:

Plutôt que d'implémenter la limitation dans le code métier, vous pouvez concevoir l'algorithme lui-même pour fonctionner à plein Steam et simplement limiter le thread qui exécute l'opération "de l'extérieur". L'approche générale est la même que dans la réponse de Ryan: Calculez un temps de suspension en fonction de l'utilisation actuelle et suspendez le thread pour cette durée avant de le reprendre à nouveau. Étant donné un processus que vous souhaitez limiter, voici la logique:

public static class ProcessManager
{
    [Flags]
    public enum ThreadAccess : int
    {
        TERMINATE = (0x0001),
        SUSPEND_RESUME = (0x0002),
        GET_CONTEXT = (0x0008),
        SET_CONTEXT = (0x0010),
        SET_INFORMATION = (0x0020),
        QUERY_INFORMATION = (0x0040),
        SET_THREAD_TOKEN = (0x0080),
        IMPERSONATE = (0x0100),
        DIRECT_IMPERSONATION = (0x0200)
    }

    [DllImport("kernel32.dll")]
    static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId);

    [DllImport("kernel32.dll")]
    static extern uint SuspendThread(IntPtr hThread);

    [DllImport("kernel32.dll")]
    static extern int ResumeThread(IntPtr hThread);

    [DllImport("kernel32.dll")]
    static extern int CloseHandle(IntPtr hThread);

    public static void ThrottleProcess(int processId, double limit)
    {
        var process = Process.GetProcessById(processId);
        var processName = process.ProcessName;
        var p = new PerformanceCounter("Process", "% Processor Time", processName);
        while (true)
        {
            var interval = 100;
            Thread.Sleep(interval);

            var currentUsage = p.NextValue() / Environment.ProcessorCount;
            if (currentUsage < limit) continue;
            var suspensionTime = (currentUsage-limit) / currentUsage * interval;
            SuspendProcess(processId);
            Thread.Sleep((int)suspensionTime);
            ResumeProcess(processId);
        }
    }

    private static void SuspendProcess(int pid)
    {
        var process = Process.GetProcessById(pid);

        if (process.ProcessName == string.Empty)
            return;

        foreach (ProcessThread pT in process.Threads)
        {
            IntPtr pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id);

            if (pOpenThread == IntPtr.Zero)
            {
                continue;
            }

            SuspendThread(pOpenThread);

            CloseHandle(pOpenThread);
        }
    }

    private static void ResumeProcess(int pid)
    {
        var process = Process.GetProcessById(pid);

        if (process.ProcessName == string.Empty)
            return;

        foreach (ProcessThread pT in process.Threads)
        {
            IntPtr pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id);

            if (pOpenThread == IntPtr.Zero)
            {
                continue;
            }

            var suspendCount = 0;

            do
            {
                suspendCount = ResumeThread(pOpenThread);
            } while (suspendCount > 0);

            CloseHandle(pOpenThread);
        }
    }
}

L'avantage de cette solution est que l'intervalle de vérification devient indépendant de la durée de votre "longue tâche". En outre, la logique métier et la logique de limitation sont séparées. Le code suspense/resume est inspiré par ce fil . Veuillez noter que l'élimination et la fin de la limitation doivent être implémentées dans la solution ci-dessus, ce n'est pas du code de production.

21
Marc

Vous pouvez écrire une classe Governor qui limite l'utilisation du processeur. Cette classe contiendrait une méthode utilitaire qui devrait être appelée régulièrement (par exemple, appeler cette fonction utilitaire dans une boucle while de votre fonction) par votre fonction liée au processeur. Le gouverneur vérifierait si le temps écoulé dépassait un seuil particulier, puis s'endormirait pendant une période de temps afin de ne pas consommer tout le CPU.

Voici une simple Java du haut de ma tête (juste pour que vous ayez l'idée) qui réduira l'utilisation du processeur à 50% si vous avez une seule fonction liée au processeur.

public class Governor
{
  long start_time;

  public Governor()
  {
    this.start_time = System.currentTimeMillis();
  }

  public void throttle()
  {
    long time_elapsed = System.currentTimeMillis() - this.start_time;

    if (time_elapsed > 100) //throttle whenever at least a 100 millis of work has been done
    {
      try { Thread.sleep(time_elapsed); } catch (InterruptedExceptione ie) {} //sleep the same amount of time

      this.start_time = System.currentTimeMillis(); //reset after sleeping.
    }
  }
}

Votre fonction liée au processeur instanciera un Governor, puis appellera simplement throttle régulièrement dans la fonction.

15
mal

Merci à tous d'avoir répondu. Je travaille sur cela et l'exe qu'il fonctionne depuis quelques heures et je veux partager pour aider les autres. J'ai écrit une classe que je vais définir et oublier dans une application WPF qui cryptera et transmettra les données au cloud, mais je ne pourrais jamais les faire interférer avec le calendrier de l'application WPF et ce dont l'application WPF a besoin en termes de ressources, que je vais également ajouter un indicateur à désactiver lorsque l'application WPF est dans son état de consommation de ressources le plus élevé. J'ai déjà fortement enfilé ce WPF avec le TPL. Cette solution a à la fois l'ensemble de priorité du processus

myProcess.PriorityClass = ProcessPriorityClass.Idle;

et le pourcentage CPU limité.

puis dans mon mainDisplay.xaml.cs je vais utiliser

ProcessManagement.StartProcess(5);

dans MainWindow ()

Et il n'y a pas de fenêtre qui s'affiche lorsque cet exe est exécuté

RedirectStandardOutput = true,  
UseShellExecute = false,
CreateNoWindow = true

dans l'initialiseur d'objet

internal class ProcessManagement
{
    private static int CpuPercentageLimit { get; set; }

    public static void StartProcess(int cpuPercent)
    {
        CpuPercentageLimit = cpuPercent;
        var stopwatch = new Stopwatch();
        while (true)
        {
            stopwatch.Reset();
            stopwatch.Start();
            var actionStart = stopwatch.ElapsedTicks;
            try
            {
                var myProcess = new Process
                {
                    StartInfo =
                    {
                        FileName = @"D:\\Source\\ExeProgram\\ExeProgram\\bin\\Debug\\ExeProgram.exe",
                        RedirectStandardOutput = true,
                        UseShellExecute = false,
                        CreateNoWindow = true
                    }
                };
                myProcess.Start();
                myProcess.PriorityClass = ProcessPriorityClass.Idle;
                myProcess.Refresh();
                myProcess.WaitForExit();
                var actionEnd = stopwatch.ElapsedTicks;
                var actionDuration = actionEnd - actionStart;
                long relativeWaitTime = (int)((1 / (double)CpuPercentageLimit) * actionDuration);
                var sleepTime = (int)((relativeWaitTime / (double)Stopwatch.Frequency) * 1000);
                Thread.Sleep(sleepTime);
                myProcess.Close();
            }
            catch (Exception e)
            {
                // ignored
            }
        }
    }
}

Dans mon application, il y a suffisamment de temps, comme 24/7/365, pour télécharger beaucoup de données, y compris des milliers d'images, mais l'interface utilisateur doit également rester active lorsqu'elle est utilisée et lorsque le système fonctionne, rien d'autre ne peut fonctionner.

4
Stephen Himes

Si vous avez un processeur multi-cœurs, vous pouvez définir l'affinité sur chaque processus pour utiliser uniquement les cœurs que vous souhaitez utiliser. C'est la méthode la plus proche que je connaisse. Mais cela ne vous permettra d'affecter que des pourcentages qui sont un facteur de 50% sur un dual core et de 25% sur un quad core.

4
Scott Hunt

Vous pouvez exécuter votre programme dans un thread avec une priorité de thread inférieure , le reste dépend de votre système d'exploitation. Avoir un processus consommer 100% de votre CPU n'est pas mauvais. Mon SETI utilise généralement tout mon temps CPU restant sans déranger mes autres programmes. Cela ne pose un problème que lorsque votre thread a la priorité sur les programmes plus importants.

3
Carra

Selon MSDN , vous ne pouvez définir qu'une priorité de thread, c'est-à-dire.

var t1 = new Thread(() => doSomething());
t1.Priority = ThreadPriority.BelowNormal;
t1.Start();

où doSomething est la fonction pour laquelle vous souhaitez créer une tête. La priorité peut être l'un des membres d'énumération ThreadPriority Lowest, BelowNormal, Normal, AboveNormal, Highest - pour une description, voir le lien MSDN ci-dessus. La priorité Normal est la valeur par défaut.

Notez que Utilisation CPU dépend également du nombre de cœurs et processeurs logiques votre CPU physique *) - et comment les threads et les processus sont affectés à ces cœurs (l'affectation à un processeur dédié est appelée "affinité du processeur" - si vous voulez en savoir plus à ce sujet, voir cette question StackOverflow ).


*) Pour le savoir, ouvrez le gestionnaire de tâches (via Ctrl+Alt+Delete - sélectionnez "gestionnaire de tâches"), allez dans Performances et sélectionnez CPU là: Sous le graphique d'utilisation, vous pouvez voir "Cores" et "Processeurs logiques".
Un cœur est une unité physique intégrée au CPU, tandis qu'un processeur logique n'est qu'une abstraction, ce qui signifie que plus le CPU est constitué de cœurs, plus il peut traiter des tâches parallèles rapidement.

3
Matt

Si votre code est en cours d'exécution, il est à 100%

Je suppose que glisser dans certains sommeil pourrait avoir un effet.

Je dois m'interroger sur ce chiffre de 2-3 minutes. Je l'ai vu aussi, et je suppose qu'il charge et initialise beaucoup de choses dont je n'ai probablement pas vraiment besoin.

2
Mike Dunlavey

Je pense honnêtement que plutôt que de vous soucier d'essayer de limiter l'utilisation du processeur par votre application, vous devriez concentrer davantage vos énergies sur le profilage de l'application pour découvrir et corriger les goulots d'étranglement et les inefficacités qui peuvent exister.

1
itsmatt