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

Posted

技术标签:

【中文标题】如何以编程方式将程序的 CPU 使用率限制在 70% 以下?【英文标题】:How can I programmatically limit my program's CPU usage to below 70%? 【发布时间】:2010-11-02 14:55:41 【问题描述】:

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

谢谢, 尼迪

【问题讨论】:

您的程序到底要做什么?您是否使用任何“自制”线程? 【参考方案1】:

首先,我同意 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);
        
    

此解决方案的好处是检查间隔与“长时间运行的任务”的持续时间无关。此外,业务逻辑和节流逻辑是分开的。悬念/简历代码的灵感来自this thread。请注意,处理和结束节流需要在上面的解决方案中实现,它不是生产代码。

【讨论】:

太棒了,这是我第一次发现这个问题时希望得到的那种低级答案。如果我理解正确,这可以限制系统上的外部第三方进程,这在某些情况下可能非常有用。 嗨@Ryan,是的,确实如此。在我的场景中,我基本上使用Process.Start 生成一个进程,然后将其关闭。您可能需要 UAC(管理员权限)来限制您自己没有产生的进程。我还没有尝试过。很高兴您喜欢它,并希望您仍然可以使用它....【参考方案2】:

根据MSDN,只能设置一个线程优先级,即

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

其中 doSomething 是您要为其创建主题的函数。优先级可以是 ThreadPriority 枚举成员之一 Lowest, BelowNormal, Normal, AboveNormal, Highest - 有关说明,请参见上面的 MSDN 链接。优先级Normal 是默认值。

注意CPU 使用率还取决于您的物理 CPU 有多少内核逻辑处理器 具有 *) - 以及如何将线程和进程分配给这些内核(分配给专用处理器称为 “处理器亲和性” - 如果您愿意要了解更多信息,see this *** question)。


*) 要找出答案,请打开任务管理器(通过 Ctrl+Alt+Delete -选择“任务管理器”),转到性能并在那里选择 CPU:在利用率图表下方,您可以看到“核心”和“逻辑处理器”。 核心是内置于 CPU 中的物理单元,而逻辑处理器只是一种抽象,这意味着您的 CPU 包含的核心越多,它处理并行任务的速度就越快。

【讨论】:

【参考方案3】:

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

myProcess.PriorityClass = ProcessPriorityClass.Idle;

并且 CPU 百分比有限。

然后在我的 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)上传大量数据,包括数千张图片,但 UI 还需要在使用时保持活动状态,并且在系统运行时,没有其他办法跑步。

【讨论】:

【参考方案4】:

这个帖子已有四年多的历史了,我仍然感到恼火的是,接受的答案批评了这个问题而不是回答它。您想要限制程序占用的 CPU 时间的正当理由有很多,我可以一一列举。

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

假设您正在编写一个后台任务,该任务旨在在后台定期索引文件。索引任务应该以较低的优先级尽可能多地使用 CPU,还是将自身限制为 25% 并根据需要花费尽可能多的时间?好吧,如果它要消耗笔记本电脑上 100% 的 CPU,CPU 会发热,风扇会启动,电池会很快耗尽,用户会很生气。如果索引服务自身受到限制,笔记本电脑可能能够以非常低的 cpu 时钟速度和电压运行完全被动冷却。

顺便说一句,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));
    

【讨论】:

+1 我讨厌所有人都说“不要那样做”。可能有超出人们理解问题的原因,但他们对待提问者就好像他只是对这些东西不熟悉,什么都不知道。顺便说一句,您在 C# 中的代码是我一直在寻找的,是的,我正在开发一个后台服务 - 根据其他答案,我应该用它杀死用户的 CPU。 @PrzemysławWrzesiński 你是对的 100%。我遇到过一种网络设备情况,它按比例记录流,并且用于提取部分的定期过程不允许占用超过 10% 的 CPU,因为主要功能是捕获大量流,而 CPU 是被征税和硬件工程不能比他们现在做的更多了。所以这是一个非常有效的问题。 " 接受的答案批评这个问题而不是回答它仍然让我很恼火" --- 我也这么认为。人们应该回答而不是问为什么有人问它。顺便说一句,我有一个非常相似的应用程序,它可以让 cpu 射击 95%,我希望它运行更长时间,但不能每次都将 CPU 飙升到 95%。 看来这种做法是有问题的。似乎 Stopwatch 对象测量经过的时间,而不仅仅是当前线程消耗的时间。 这会导致 100% 的使用率,然后是 0% 的使用率,然后是 100% 的使用率,以此类推吗?有什么办法让它保持在稳定的 30%?【参考方案5】:

您可以编写一个 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.
    
  

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

【讨论】:

-1。这是一个可怕的想法。您的应用程序应该使用尽可能多的系统资源(在合理范围内,使用 Windows 中的所有句柄将是愚蠢的)。让操作系统管理此类分配。 不一定 - 您不能依赖其他外部事物来管理您的应用程序如何为您运行。我以前在很多地方都看到过这种情况——甚至某些版本的 SQL Server 也有资源管理器。例如,如果您的应用程序正在提供服务,但包含维护可能受 CPU 限制的应用程序的后台任务,则后台任务不应占用所有 CPU,同时拒绝为用户提供服务。你不能把这样的实现留给操作系统。管理。这只是原因之一。还有很多其他的。【参考方案6】:

如果您有一个多核处理器,您可以将每个进程的 Affinity 设置为仅使用您希望它使用的内核。这是我所知道的最接近的方法。但它只允许您分配双核上 50% 和四核上 25% 的百分比。

【讨论】:

这仅对多线程进程有任何影响,除非您将多个进程的亲和性设置为一个内核并让另一个内核空闲。让一个线程在四核 CPU 上使用 100% 的一个内核与让它在单核 CPU 上使用 100% 的一个内核没有什么不同,如果没有其他东西在运行的话。唯一改变的是 Windows 任务管理器将平均负载除以可用内核数。【参考方案7】:

我认为您需要做的是了解应用程序中的性能问题,而不是试图限制 CPU 使用率。 您可以使用 Visual Studio Profiler 查看为什么您的应用程序首先在 2-3 分钟内占用 100% CPU。这应该会揭示您应用中的热点,然后您就可以解决此问题。

如果您一般询问如何在 Windows 中进行资源限制,那么您可以查看“任务”对象,作业对象允许您设置限制,例如工作集、进程优先级...等。

您可以在此处查看作业对象文档 http://msdn.microsoft.com/en-ca/library/ms684161(VS.85).aspx 希望这可以帮助。 谢谢

【讨论】:

【参考方案8】:

如果没有其他任务在运行,那么您的应用使用所有可用的 cpu 容量是否有问题?它是可用的,就像在中一​​样,它就在那里并且可以免费使用。所以用它吧!

如果您以某种方式限制任务的 CPU 使用率,则需要更长的时间才能完成。但是它仍然需要相同数量的 cpu 周期,所以你什么也得不到。你只是减慢了你的应用程序。

不要这样做。甚至不要尝试。你没有理由这样做。

【讨论】:

是的,“系统空闲任务”为什么要这么好玩? @paxdiablo 因为电池寿命。 假设您将 cpu 使用率降低到 50%,这反过来又降低了 50% 的功耗。让我们进一步假设 CPU 使用量减少 50% 的计算将花费 100% 使用量的两倍。在这个(诚然相当简单的)场景中,您的整体功耗根本不会改变。因此,我认为这不值得花时间思考。 这么多假设,但实际上随着 CPU 速度的降低,功耗降低得更快。 对不起,但这个答案中的每一件事都不是完全正确的。 “你没有理由应该”仅仅因为我们当时缺乏想象力,并不意味着没有理由。其中之一是:superuser.com/questions/214566 但还有更多。【参考方案9】:

如果你的代码完全在运行,它是 100%

我想在某些睡眠中滑倒可能会产生影响。

我不得不怀疑那个 2-3 分钟的数字。我也见过它,我想它正在加载和初始化很多我可能并不真正需要的东西。

【讨论】:

【参考方案10】:

老实说,我认为与其担心尝试限制应用程序的 CPU 使用率,不如将更多精力集中在分析应用程序上,以发现和纠正可能存在的瓶颈和低效问题。

【讨论】:

【参考方案11】:

您可以在threadpriority 较低的线程中运行您的程序,其余的取决于您的操作系统。让一个进程吃掉 100% 的 CPU 也不错。我的 SETI 通常会占用我所有剩余的 CPU 时间,而不会打扰我的其他程序。只有当您的线程优先于更重要的程序时才会出现问题。

【讨论】:

IIRC SETI 仅使用备用时钟周期,因此当机器显示 100% cpu 时它仍然响应,并且只要其他东西需要处理能力并开始使用 cpu,备用时钟周期的数量就会减少所以 SETI 受到限制而不是其他进程。 这是因为 SETI 以允许的最低线程优先级运行,并且操作系统会处理节流。 “备用”的定义是什么? SETI 通过将其优先级设置得非常低(低于您希望保持响应的其他程序)来使用“备用”的 CPU 周期。如果有更高优先级的进程正在运行,那么 SETI 将不会获得任何周期。【参考方案12】:

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

另见:Windows Equivalent of ‘nice’

【讨论】:

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

以上是关于如何以编程方式将程序的 CPU 使用率限制在 70% 以下?的主要内容,如果未能解决你的问题,请参考以下文章

如何通过android中的软键盘以编程方式将我的EditText输入限制为反斜杠(/),tild(〜)等特殊字符

以编程方式在iPhone 6中使用指导访问

如何以编程方式确定 YouTube 视频是不是有年龄限制?

如何以编程方式获取用户在 Android OS 配置上设置的数据使用限制?

如何以编程方式更改 UIImageView 的高度?

以编程方式设置应用程序的处理器关联