Как я могу программно ограничить использование процессора моей программой до уровня ниже 70%? - PullRequest
44 голосов
/ 13 июня 2009

В последнее время я становлюсь более ориентированным на здоровье, когда создаю свою программу, я заметил, что большинству программ требуется 2 или 3 минуты для выполнения, и когда я проверяю планировщик задач, я вижу, что они потребляют 100% ЦП использование, я могу ограничить это использование программно в коде? Это, безусловно, позволит мне запускать несколько программ одновременно.

Спасибо, Nidhi

Ответы [ 12 ]

78 голосов
/ 27 ноября 2014

Этой теме более четырех лет, и меня все еще раздражает, что принятый ответ критикует вопрос, а не отвечает на него. Есть много веских причин, по которым вы хотели бы ограничить процессорное время, затрачиваемое вашей программой, я могу перечислить некоторые из них в верхней части головы.

Может показаться пустой тратой не использовать все доступные свободные циклы ЦП, но этот менталитет несовершенен. В отличие от старых процессоров, большинство современных процессоров не работают с фиксированной тактовой частотой - у многих есть режимы энергосбережения, в которых они понижают тактовую частоту и напряжение процессора при низкой нагрузке . Процессоры также потребляют больше энергии при выполнении вычислений, чем при выполнении NOOP. Это особенно актуально для ноутбуков, которым требуется вентиляторы для охлаждения процессора, когда он находится под высокой нагрузкой. Выполнение задачи на 100% в течение короткого времени может потреблять гораздо больше энергии, чем выполнение задачи на 25% в четыре раза дольше.

Представьте, что вы пишете фоновое задание, предназначенное для периодической индексации файлов в фоновом режиме. Должна ли задача индексирования использовать как можно больше ресурсов ЦП с более низким приоритетом, или довести себя до 25% и занять столько времени, сколько нужно? Что ж, если бы он потреблял 100% ЦП на ноутбуке, ЦП нагревался, вентиляторы включались, и батарея разряжалась довольно быстро, и пользователь раздражался. Если служба индексирования сама себя удушила, ноутбук может работать с полностью пассивным охлаждением при очень низкой тактовой частоте процессора и напряжении.

Кстати, служба индексации Windows теперь регулирует себя в более новых версиях Windows, чего никогда не делала в более старых версиях. Пример службы, которая все еще не ограничивает себя и часто раздражает людей, приведена в разделе Модуль установщика Windows.

Пример того, как внутренне управлять частью вашего приложения в 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));
    }
}
29 голосов
/ 13 июня 2009

Это не ваше дело ... Задача операционной системы - распределять процессорное время между запущенными процессами. Если вы хотите, чтобы другие процессы сначала взломали свою работу, просто уменьшите приоритет вашего собственного процесса, изменив значение Process.PriorityClass для него.

См. Также: Эквивалент Windows «Ницца»

19 голосов
/ 26 октября 2015

Прежде всего, я согласен с Райаном в том, что этот вопрос совершенно актуален, и есть случаи, когда приоритеты потоков вообще не являются достаточными. Другие ответы кажутся очень теоретическими и бесполезными в ситуациях, когда приложение правильно спроектировано, но все еще нуждается в регулировании. Райан предлагает простое решение для случаев, когда относительно короткие задачи выполняются с высокой частотой. Однако существуют случаи, когда задача занимает очень длительное время (скажем, минуту или около того), и вы не можете или не хотите разбивать ее на более мелкие куски, между которыми вы можете выполнять регулирование. Для этих случаев может быть полезно следующее решение:

Вместо того, чтобы реализовывать регулирование в бизнес-коде, вы можете спроектировать сам алгоритм, чтобы он работал на полную мощность и просто регулировал поток, который выполняет операцию «извне». Общий подход такой же, как и в ответе Райана: рассчитайте время приостановки на основе текущего использования и приостановите поток на этот промежуток времени, прежде чем возобновить его снова. Учитывая процесс, который вы хотите ограничить, это логика:

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);
        }
    }
}

Преимущество этого решения заключается в том, что интервал проверки становится независимым от продолжительности вашей "длительной задачи". Кроме того, бизнес-логика и логика регулирования разделены. Код приостановки / возобновления навеян этой веткой . Обратите внимание, что устранение и прекращение регулирования должно быть реализовано в приведенном выше решении, это не рабочий код.

15 голосов
/ 10 декабря 2009

Вы можете написать класс Governor, который ограничивает использование процессора. Этот класс будет содержать служебный метод, который должен вызываться на регулярной основе (например, вызывать эту служебную функцию в цикле while вашей функции) с помощью функции, связанной с процессором. Регулятор будет проверять, превысило ли прошедшее время определенное пороговое значение, а затем в течение некоторого времени бездействовать, чтобы не использовать весь процессор.

Вот простая реализация Java, которая стоит передо мной (просто чтобы вы поняли идею), которая увеличит нагрузку на процессор до 50%, если у вас есть однопоточная функция, связанная с процессором.

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.
    }
  }
}

Ваша связанная с процессором функция создаст экземпляр Governor, а затем просто вызовет throttle на регулярной основе внутри функции.

4 голосов
/ 13 апреля 2017

Спасибо всем за ответ. Я работал над этим и exe, он работает в течение нескольких часов и хочу поделиться, чтобы помочь другим. Я написал класс, который собираюсь установить и забыть в приложении WPF, которое будет шифровать и отправлять данные в облако, но я никогда не мог позволить, чтобы он когда-либо влиял на синхронизацию приложения WPF и потребности приложения WPF в способе ресурсов, который я также собираюсь добавить, чтобы отключить флаг, когда приложение WPF находится в состоянии максимального потребления ресурсов. Я уже сильно связал этот WPF с TPL. Это решение имеет оба набора приоритетов процесса

myProcess.PriorityClass = ProcessPriorityClass.Idle;

и процент загрузки процессора ограничен.

тогда в моем mainDisplay.xaml.cs я буду использовать

ProcessManagement.StartProcess(5);

в MainWindow ()

И при запуске этого exe-файла не появляется окно

RedirectStandardOutput = true,  
UseShellExecute = false,
CreateNoWindow = true

в объекте инициализатора

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
            }
        }
    }
}

В моем приложении достаточно времени, например, 24/7/365, для загрузки большого количества данных, включая тысячи изображений, но пользовательский интерфейс также должен оставаться активным при использовании и во время работы системы, больше ничего не может быть Бег.

3 голосов
/ 05 ноября 2010

Если у вас многоядерный процессор, вы можете настроить Affinity для каждого процесса, чтобы использовать только те ядра, которые вы хотите использовать. Это самый близкий из известных мне методов. Но это позволит вам назначить только проценты, которые составляют 50% для двухъядерного процессора и 25% для четырехъядерного.

2 голосов
/ 07 мая 2018

Согласно MSDN , вы можете установить только приоритет потока, т. Е.

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

где doSomething - функция, для которой вы хотите создать thead. Приоритет может быть одним из членов перечисления ThreadPriority Lowest, BelowNormal, Normal, AboveNormal, Highest - описание см. Выше в ссылке MSDN. Приоритет Normal является значением по умолчанию.

Обратите внимание , что загрузка ЦП также зависит от того, сколько ядер и логических процессоров вашего физического ЦП имеет *) - и как потоки и процессы назначаются этим ядрам (назначение выделенному процессору называется «привязка к процессору» - если вы хотите узнать больше об этом, см. Этот вопрос StackOverflow ).


*) Чтобы это выяснить, откройте диспетчер задач (через Ctrl + Alt + Удалить - выберите "диспетчер задач" "), зайдите в Performance и выберите CPU там: ниже графика загрузки вы можете увидеть" Cores "и" Logical Processors ".
Ядро - это физическая единица, встроенная в ЦП, а логический процессор - это просто абстракция, а это означает, что чем больше ядер состоит из вашего ЦП, тем быстрее он может обрабатывать параллельные задачи.

2 голосов
/ 13 июня 2009

Если ваш код работает вообще, он равен 100%

Я полагаю, что проскальзывание в некоторых снах может иметь эффект.

Я должен задаться вопросом об этой 2-3-минутной фигуре. Я тоже это видел, и я полагаю, что он загружает и инициализирует много вещей, которые мне, вероятно, на самом деле не нужны.

2 голосов
/ 13 июня 2009

Вы можете запустить вашу программу в потоке с более низким приоритетом потока , остальное зависит от вашей операционной системы. Процесс, поедающий 100% вашего процессора, неплох. Мой SETI обычно занимает все оставшееся у меня процессорное время, не мешая другим программам. Проблема возникает только тогда, когда ваш поток получает приоритет над более важными программами.

1 голос
/ 13 июня 2009

Я, честно говоря, думаю, что вместо того, чтобы беспокоиться о попытке ограничить загрузку ЦП вашим приложением, вам следует сосредоточить больше усилий на профилировании приложения для выявления и устранения узких мест и недостатков, которые могут существовать.

...