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

时间:2009-06-12 22:07:05

标签: c# performance cpu-usage system.diagnostics

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

谢谢, 尼迪

12 个答案:

答案 0 :(得分:75)

这个帖子已经超过四年了,我仍然很惹恼我接受的答案批评这个问题而不是回答它。有许多正当理由可以限制程序占用的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));
    }
}

答案 1 :(得分:31)

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

另见:Windows Equivalent of ‘nice’

答案 2 :(得分:19)

首先,我同意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。请注意,处理和结束限制需要在上面的解决方案中实现,它不是生产代码。

答案 3 :(得分:15)

您可以编写一个限制CPU使用率的Governor类。该类将包含一个实用程序方法,该方法应该由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

答案 4 :(得分:4)

感谢大家的回答。我一直在研究这个和它运行几个小时的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在使用时也需要保持活动状态,当系统运行时,没有其他任何东西可以运行。

答案 5 :(得分:3)

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

答案 6 :(得分:2)

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

答案 7 :(得分:2)

如果您的代码正在运行,那么它是100%

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

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

答案 8 :(得分:2)

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

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

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

注意 CPU使用率还取决于内核逻辑处理器 物理CPU的数量 *) - 以及如何将线程和进程分配给这些核心(对专用处理器的分配称为&#34;处理器关联性&#34; - 如果您想了解更多相关信息,see this StackOverflow question)。

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

答案 9 :(得分:1)

老实说,我认为而不是担心尝试限制应用程序的CPU利用率,您应该将更多的精力放在分析应用程序上,以发现并纠正可能存在的瓶颈和低效率。

答案 10 :(得分:-1)

如果没有其他任务正在运行,您的应用使用可用的所有CPU容量是否有误?它是可用的,就像在那里一样,可以免费使用。所以使用它!

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

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

答案 11 :(得分:-1)

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

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

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