【问题标题】:Limit CPU usage in conjunction with Parallel.ForEach结合 Parallel.ForEach 限制 CPU 使用率
【发布时间】:2018-12-04 15:26:44
【问题描述】:

我需要什么:

我需要在多线程中运行一组方法,每个内核最多 1 个线程。这些线程中的每一个都需要具有限制功能才能限制所有线程的 CPU 使用率。如果我在运行 3 个线程的 4 个内核上指定 10%(因为我选择 3 个是最大值),那么所有 3 个线程每个内核的 CPU 使用率不得超过 10%。就像 Steam 这样的应用程序,您可以限制下载速度,但想象一下您将有多个 Internet 连接来表示多核。

我尝试了什么:

我尝试过使用Task,但我不知道如何管理它创建的最大线程数,甚至不知道它们各自使用哪个内核。

我最终得到了什么:

我最终找到了 Parallel.ForEachParallelOptions.MaxDegreeOfParallelism 的组合,这让我完全可以控制线程数量。我知道我有多少核心,我可以轻松地制作一些东西让用户选择要使用的核心数量。我的测试得出结论,Parallel.ForEach 属性按顺序在每个核心上工作,一旦所有核心都收到了一些东西,它就会循环回来并再次重新分配。这是完美的。如果我在 4 核上设置 3 的并行度,我会在 3 个不同的核上得到 3 个线程,而在 4 核上设置 5 的并行度则 3 核将有 1 个线程,1 核将有 2 个。这种行为正是我需要的。这种方法不是一成不变的,我愿意改变。

CPU 限制

现在使用Parallel.ForEach 使 cpu 100% 的时间工作,这在那种紧凑的环境中是一个大问题,在很长一段时间内,在许多计算机上任何超过 40-50% 的使用都会增加如此多的功耗它会在建筑物中触发严重警报,我们需要低于该警报。无论如何,除了在紧密循环中执行 thread.sleep 之外,我没有找到任何解决方案,但这仍然使 cpu 在几秒钟内运行 100%,然后在更长的时间内接近 0%,然后它一遍又一遍地这样做。这没有用。我在一个只有 150 台计算机的房间里进行了一项小型测试,其中一半在几乎相同的时间以 100% 的速度飙升,消耗了太多的电量。我的第二个测试是仅使用 8 个或更多内核并仅在 1 个内核上运行,这样可以大大减少使用量。

优先级

我终于测试了进程优先级,但这并没有更好。是的,该进程接收的 CPU 时间更少,但当我获得电源时,cpu 仍以 100% 运行。除非我在处理可能限制 CPU 的优先级时错过了关键选项。

终于

我感觉我与Parallel.ForEach 非常接近,我只是错过了每个内核的 CPU 限制。我只缺少一件事吗?我知道使用Thread.Sleep 是完全愚蠢的,因为它实际上限制了cpu 的使用时间而不是数量。

对于那些想要代码的人

这又是原型,所以代码几乎没用。它所做的一切都是在许多 cpu 上运行代码,直到完成

static void Main(string[] args)
{
    var items = Enumerable.Range(0, int.MaxValue - 100);

    Parallel.ForEach(
             items,
             new ParallelOptions { MaxDegreeOfParallelism = 4 },
             value => { Calculate(value); }
     );
}

private static void Calculate(int value)
{
    System.Threading.Thread.SpinWait(100000);
    var test = value;
    test *= -1;
    Console.WriteLine(test);
}

【问题讨论】:

  • SpinWait 就是这样做的。
  • @DanielA.White 所以你的意思是技术上SpinWait 应该给我一种方法来减少我的 CPU 使用率?我发布的代码从 1000 开始并添加了零,直到我看到差异并且它没有改变任何东西。 CPU 仍然达到每核 60-70%。
  • 没有我说旋转等待会加载。
  • @DanielA.White 好吧,我明白你的意思。无论如何SpinWait 我最初把它放在那里替换Thread.Sleep 看看它是如何影响CPU 使用率的。也不好。

标签: c#


【解决方案1】:

您可以使用Windows Job Objects 来限制整个进程(或进程树)的 CPU 使用率。您可以限制整个进程或启动您限制的子工作进程。

这需要 PInvoke(这里不太难)并且不允许您控制特定线程(这里似乎不需要)。

或者,您需要自己进行节流(因为您说过仅控制线程数对您不起作用)。我会以PauseToken 为基础。任何将油门限制到 75% 的简单方法都是:

var pauseToken = new PauseToken();

//Use pauseToken on all threads that consume a lot of CPU

Task.Run(async () => {
 while (true) {
  await Task.Delay(250); //run
  pauseToken.Pause();
  await Task.Delay(750); //pause
  pauseToken.Resume();
 }
});

您甚至可以动态调整限制百分比。每秒读取设备的电源使用情况。如果高于某个阈值,则将暂停百分比增加 10%。如果低于,则将暂停百分比降低 5%。这样您就可以非常轻松地定位特定的瓦数。

【讨论】:

  • 听起来很不错。我确实通过 USB 探针了解了计算机的实际电源使用情况,该探针以每 1/6000 秒的 8 个样本的速率更新。也许1个小问题。有没有办法强制运行 Task 的核心,或者最简单的方法是真正创建一个线程并在那里设置 CPU 亲和力?
  • 任务没有 CPU 亲和性。你需要通过线程来做到这一点。使用LongRunning 选项,每个任务都有一个线程。然后,您可以在第一行的该任务代码中设置 CPU 亲和性。这为您提供了线程控制任务的便利。或者,使用 TaskScheduler,但工作方式会略有不同。
  • 您对 PauseToken 的使用与链接完全不匹配。我得花点时间看看 Windows 作业对象。
  • @Franck 是的,我只是写了伪代码。这是相同的概念,文章写得很好。如果您在理解上有困难,请随时询问。
  • 他们自己的样本没有像我描述的那样工作。暂停实际上并没有暂停当前进程,这在我的情况下很糟糕。把它放在一个单独的线程中是最糟糕的,也不会暂停主线程。我将更多地研究窗口作业对象。我有其他人正在研究测试已经存在的其他软件,这些软件可以在您想要的任何应用程序上按百分比限制 CPU 使用率。我觉得很奇怪,使用第三方选项似乎超级简单,而使用 C# 却很复杂
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-02-26
  • 2017-01-25
  • 2012-07-06
  • 2016-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多