最近,我在构建程序时变得更加注重健康,我观察到大多数程序需要 2 或 3 分钟才能执行,当我检查任务调度程序时,我发现它们消耗了 100% 的 CPU 使用率,可以我在代码中以编程方式限制这种用法?这肯定能让我在给定时间运行多个程序。
谢谢,尼迪
最近,我在构建程序时变得更加注重健康,我观察到大多数程序需要 2 或 3 分钟才能执行,当我检查任务调度程序时,我发现它们消耗了 100% 的 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));
}
}
首先,我同意 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);
}
}
}
此解决方案的好处是检查间隔变得独立于“长时间运行的任务”的持续时间。此外,业务逻辑和节流逻辑是分开的。悬念/简历代码受此线程启发。请注意,处理和结束节流需要在上面的解决方案中实现,它不是生产代码。
您可以编写一个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
在函数内定期调用。
谢谢大家的回答。我一直在研究这个及其运行了几个小时的 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 还需要在使用时保持活动状态,并且当系统运行时,不能运行其他任何东西。
如果您有一个多核处理器,您可以将每个进程的 Affinity 设置为仅使用您希望它使用的内核。这是我所知道的最接近的方法。但它只允许您在双核上分配 50% 的百分比,在四核上分配 25% 的百分比。
根据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
老实说,我认为与其担心尝试限制应用程序的 CPU 使用率,不如将更多的精力集中在分析应用程序上,以发现和纠正可能存在的瓶颈和低效问题。
如果您的代码完全在运行,它是 100%
我想在某些睡眠中滑倒可能会产生影响。
我不得不怀疑那个 2-3 分钟的数字。我也见过它,我想它正在加载和初始化很多我可能并不真正需要的东西。
如果没有其他任务在运行,那么您的应用程序使用所有可用的 cpu 容量是否有问题?它是可用的,因为它在那里并且可以免费使用。所以使用它!
如果您以某种方式限制任务的 CPU 使用率,则需要更长的时间才能完成。但是它仍然需要相同数量的 cpu 周期,所以你什么也得不到。你只是减慢你的应用程序。
不要这样做。甚至不要尝试。没有理由你应该这样做。
我认为您需要做的是了解应用程序中的性能问题,而不是试图限制 CPU 使用率。您可以使用 Visual Studio Profiler 查看为什么您的应用程序首先在 2-3 分钟内占用 100% CPU。这应该会揭示您应用程序中的热点,然后您就可以解决此问题。
如果您一般询问如何在 Windows 中进行资源限制,那么您可以查看“任务”对象,作业对象允许您设置限制,例如工作集、进程优先级......等。
您可以在此处查看作业对象文档 http://msdn.microsoft.com/en-ca/library/ms684161(VS.85).aspx 希望这会有所帮助。谢谢