与Parallel.ForEach一起限制CPU使用率

时间:2018-12-04 15:26:44

标签: c#

我需要什么:

我需要在多线程中运行方法数组,每个内核最多有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);
}

1 个答案:

答案 0 :(得分:0)

您可以使用Windows Job Objects来限制整个进程(或进程树)的CPU使用率。您可以限制整个过程,也可以启动自己限制的童工过程。

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

或者,您需要自己进行节流(因为您声明仅控制线程数对您不起作用)。我将其基于PauseToken。将油门调到75%的任何简单方法是:

def find(A, v):
    i = 0

    for row in A:
        num = binary_search(v, row, 0, len(row) - 1)
        if num != -1:
            return(i, num)
        i += 1

    return None


def binary_search(v, row, left, right):
    if left > right:
        return -1

    mid = (left + right) // 2

    if row[mid] == v:
        return mid

    elif row[mid] > v:
        return binary_search(v, row, left, mid - 1)

    elif row[mid] < v:
        return binary_search(v, row, mid + 1, right)

您甚至可以动态调整节流百分比。每秒读取设备的电源使用情况。如果它超过某个阈值,则将暂停百分比增加10%。如果低于此值,请将暂停百分比降低5%。这样,您可以轻松地确定特定的瓦数。