如何以编程方式将程序的CPU使用率限制在70%以下?

Nid*_*dhi 44 c# performance system.diagnostics cpu-usage

最近,我在构建我的程序时变得更加健康,我观察到大多数程序需要2或3分钟才能执行,当我检查任务调度程序时,我发现它们消耗了100%的CPU使用率,可以我在代码中以编程方式限制了这种用法?这肯定会让我在给定的时间运行多个程序.

谢谢,Nidhi

Rya*_*yan 81

这个帖子已经超过四年了,我仍然很恼火,接受的答案批评这个问题而不是回答它.有许多有效的理由要限制程序占用的CPU时间,我可以列出一些我的头脑.

不使用所有可用的CPU周期似乎是浪费,但这种心态是有缺陷的.与较旧的CPU不同,大多数现代CPU不以固定的时钟速度运行 - 许多都具有省电模式,在负载较低时它们会降低时钟速度和CPU电压.执行计算时CPU也会比运行NOOP时消耗更多功率.这与需要风扇在高负载时冷却CPU的笔记本电脑尤其相关.在短时间内以100%运行任务可以比使用25%的任务执行任务多四倍的能量.

想象一下,您正在编写一个后台任务,旨在定期在后台索引文件.索引任务是否应该以较低的优先级使用尽可能多的CPU,或者将自己限制在25%并且需要多长时间?好吧,如果要在笔记本电脑上消耗100%的CPU,CPU就会升温,风扇会启动,电池会很快耗尽,用户会感到恼火.如果索引服务自身受到限制,笔记本电脑可能能够以非常低的CPU时钟速度和电压完全被动冷却.

顺便提一下,Windows索引服务现在会在较新版本的Windows中限制自己,这在旧版本中从未发生过.有关仍然不会限制自身且经常使人烦恼的服务示例,请参阅Windows Installer模块.

如何在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));
    }
}
Run Code Online (Sandbox Code Playgroud)

  • +1我恨所有人都可以说是"不要那样做".除了人们对某个问题的理解之外,可能还有其他原因,但是他们对待问题的人就好像他刚刚对这些东西不熟悉并且什么都不知道.顺便说一句你在C#中的代码是我一直在寻找的,是的,我正在开发一个后台服务 - 根据其他答案,我应该用它来杀死用户的CPU. (17认同)
  • "我仍然惹恼我,接受的答案批评这个问题而不是回答它" - 我也是这样想的.人们应该回答而不是问为什么有人问它.顺便说一下,我有一个非常相似的应用程序,使CPU运行95%,我希望它运行更长,但不能每次CPU达到95%. (5认同)
  • @PrzemysławWrzesiński你是对的100%.我遇到了一个网络设备的情况,它在AT SCALE中记录流,并且提取部分的定期过程不允许占用CPU的10%以上,因为主要功能是捕获大量流并且CPU是被征税和硬件工程不能做他们正在做的事情.所以这是一个非常有效的问题. (2认同)

Sho*_*og9 30

这不是您关心的问题......操作系统的工作是在运行的进程之间分配处理器时间.如果你想让其他进程首先完成他们的工作,那么只需通过修改Process.PriorityClass它的值来降低自己进程的优先级.

另请参阅:Windows等效'nice'

  • 如果可以使用并且可以使用100%,那将是浪费.当然,这可能反映了程序中的设计不佳,因此请确保您没有任何Schlemiel算法.请参见http://en.wikipedia.org/wiki/Schlemiel_the_painter%27s_Algorithm (17认同)
  • 有些情况下您明确不想使用所有可用的CPU.一个这样的情况(我的情况:))是一个程序,它在桌面的背景下工作,专用于用户,你不想阻止用户共同工作,但你想要10-20%的CPU,这将是不要让他的机器停下来.当你在办公室里有超过100个台式机具有可怕的特性(对于机器工程师)并且你有并行任务时,这是完美的 - 通过域控制器将你的应用程序放在它们的Windows服务上,但限制你的应用程序,这样你就不会停止工程师的PC !:) (3认同)
  • 那些类似于游戏的东西,你可能希望游戏循环在游戏暂停或最小化时变得空闲?当然,减少利用率是合适的. (2认同)
  • @Matt:嗯,是的,如果你无事可做,那就什么都不做!但是,您还是最好让操作系统处理这个问题(通过某种阻塞系统调用)——您只是不能假设您的程序有足够的关于整个系统的信息来有效地限制自己,并且可能最终会适得其反(想象一下,一个游戏在最小化时将其循环降低到 1% 的 CPU……听起来很棒,对吧?但这仍然足以烧毁您的笔记本电脑电池…… (2认同)
  • +1.您的系统应尽可能多地使用CPU和内存,否则会浪费.操作系统应决定如何分配这些资源,而不是任何单个程序.事实上,我相信Windows会用内存做到这一点.如果你不使用它,它会开始将它自己用于磁盘缓冲区等. (2认同)

Mar*_*arc 20

首先,我同意Ryan的看法,这个问题是完全有效的,并且有些情况下线程优先级根本不够.其他答案看起来非常理论化,在应用程序设计合理但仍需要加以限制的情况下没有实际用途.Ryan为在高频率下执行相对较短任务的情况提供了简单的解决方案.但是,有些情况下,当任务需要长时间(比如一分钟左右)时,您不能或不想将其分成较小的块,在这些块之间可以进行限制.对于这些情况,以下解决方案可能会有所帮助

而不是在业务代码中实施限制,您可以将算法本身设计为全速工作,并简单地限制"从外部"运行操作的线程.一般方法与Ryan的答案相同:根据当前使用情况计算暂停时间,并在再次恢复之前暂停该时间跨度.给定一个您想要限制的进程,这是逻辑:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

此解决方案的好处是检查间隔与"长时间运行的任务"的持续时间无关.此外,业务逻辑和限制逻辑是分开的.悬念/简历代码的灵感来自于这个主题.请注意,处理和结束限制需要在上面的解决方案中实现,它不是生产代码.


mal*_*mal 15

您可以编写一个Governor限制CPU使用率的类.该类将包含一个实用程序方法,该方法应该由CPU绑定函数定期调用(例如,在函数的while循环中调用此实用程序函数).调控器将检查经过的时间量是否超过特定阈值,然后休眠一段时间以便不消耗所有CPU.

这是一个简单的Java实现,如果您有一个单独的线程CPU绑定功能,那么可以将CPU使用率限制为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.
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

您的CPU绑定函数将实例化a Governor,然后throttle在函数内定期调用.

  • 不一定 - 您不能依赖于管理应用程序运行方式的其他外部事务.我之前看过很多地方 - 甚至某些版本的SQL Server都有资源调控器.例如,如果您的应用程序提供服务,但包括维护可能受CPU限制的应用程序的后台任务,则后台任务不应占用所有CPU,同时拒绝为用户提供服务.您不能将这样的实现留给操作系统来管理.这只是一个原因.还有很多其他的. (17认同)
  • -1.这是一个糟糕的主意.您的应用程序应尽可能多地使用系统资源(在合理范围内,使用Windows中的所有句柄都会很愚蠢).离开操作系统来管理这样的分配. (4认同)

Ste*_*mes 5

谢谢大家的回答。我一直在研究这个,它运行了几个小时的 exe 并希望分享以帮助他人。我写了一个类,我将在一个 WPF 应用程序中设置并忘记它,该应用程序将加密并将数据推送到云,但我永远不会让它干扰 WPF 应用程序的时间以及 WPF 应用程序需要什么在资源方面,当 WPF 应用程序处于最高资源消耗状态时,我还将添加一个标志以禁用。我已经用 TPL 高度线程化了这个 WPF。此解决方案具有进程的优先级集

myProcess.PriorityClass = ProcessPriorityClass.Idle;
Run Code Online (Sandbox Code Playgroud)

并且 CPU 百分比有限。

然后在我的 mainDisplay.xaml.cs 中我将使用

ProcessManagement.StartProcess(5);
Run Code Online (Sandbox Code Playgroud)

在主窗口()

并且运行该exe时没有弹出窗口

RedirectStandardOutput = true,  
UseShellExecute = false,
CreateNoWindow = true
Run Code Online (Sandbox Code Playgroud)

在对象初始化器中

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
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在我的应用程序中,有足够的时间(例如 24/7/365)上传大量数据,包括数千张图像,但 UI 也需要在使用时保持活动状态,并且当系统运行时,没有其他东西可以运行。


归档时间:

查看次数:

33050 次

最近记录:

6 年,9 月 前