53

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

谢谢,尼迪

4

12 回答 12

117

该线程已有四年多的历史了,接受的答案批评问题而不是回答问题仍然让我很恼火。您想要限制程序占用的 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));
    }
}
于 2014-11-27T03:18:56.263 回答
25
于 2009-06-12T22:10:34.233 回答
25

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

此解决方案的好处是检查间隔变得独立于“长时间运行的任务”的持续时间。此外,业务逻辑和节流逻辑是分开的。悬念/简历代码受此线程启发。请注意,处理和结束节流需要在上面的解决方案中实现,它不是生产代码。

于 2015-10-26T10:51:47.790 回答
16

您可以编写一个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 绑定函数将实例化 a Governor,然后throttle在函数内定期调用。

于 2009-12-10T05:00:51.227 回答
5

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

myProcess.PriorityClass = ProcessPriorityClass.Idle;

并且CPU百分比有限。

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

ProcessManagement.StartProcess(5);

在主窗口()

运行该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 还需要在使用时保持活动状态,并且当系统运行时,不能运行其他任何东西。

于 2017-04-13T19:45:36.167 回答
4

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

于 2010-11-05T02:17:30.537 回答
3
于 2009-06-12T22:11:21.157 回答
3

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

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

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

请注意CPU 使用率还取决于您的物理 CPU有多少核心逻辑处理器*) - 以及线程和进程如何分配给这些核心(分配给专用处理器称为“处理器关联” - 如果您想知道有关更多信息,请参阅此 StackOverflow 问题)。


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

于 2018-05-07T12:19:46.113 回答
1

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

于 2009-06-12T22:17:04.420 回答
1

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

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

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

于 2009-06-12T22:17:24.780 回答
-2

如果没有其他任务在运行,那么您的应用程序使用所有可用的 cpu 容量是否有问题?它是可用的,因为它在那里并且可以免费使用。所以使用它!

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

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

于 2009-06-12T22:28:56.437 回答
-2

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

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

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

于 2009-06-13T20:34:37.123 回答