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

问题描述 投票:42回答:12

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

谢谢,财务

c# performance cpu-usage system.diagnostics
12个回答
29
投票

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

See also: Windows Equivalent of ‘nice’


1
投票

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


-1
投票

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

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

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


-1
投票

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

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

你可以在这里查看Job对象文档http://msdn.microsoft.com/en-ca/library/ms684161(VS.85).aspx希望这会有所帮助。谢谢


79
投票

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

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


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
投票

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


3
投票

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


2
投票

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


2
投票

如果代码运行完毕,则为100%

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

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


2
投票

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

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

doSomething是你想要创建thead的功能。优先级可以是ThreadPriority枚举成员之一Lowest, BelowNormal, Normal, AboveNormal, Highest - 有关说明,请参阅上面的MSDN链接。优先级Normal是默认值。

请注意,CPU使用率还取决于物理CPU具有多少核心和逻辑处理器*) - 以及如何将线程和进程分配给这些核心(对专用处理器的分配称为“处理器关联性” - 如果您想知道更多关于这一点,see this StackOverflow question)。


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

© www.soinside.com 2019 - 2024. All rights reserved.