8

问题描述

  • 该问题的目标是在Windows上编写一个程序,以将 CPU 使用率保持在50%左右。
  • 在 Windows 上,我们可以使用任务管理器Perfmon.exe来监控 CPU 使用率。
  • CPU 使用率应该在 50% 附近,因为操作系统中还有很多其他任务,我们只取 CPU 的大概使用率。
  • CPU 可以是多核处理器或单核处理器,因此首选通用解决方案

问题的根源

最初的问题来自编程之美的第 1.1 章

个人在这个问题上的努力

环境

  • 处理器:Intel i5-3470,4核,4线程
  • 系统:Windows 7
  • 开发环境:Visual Studio 2010,boost库

第一次尝试

我的第一次尝试没有考虑多核和多线程,所以它没有工作,但它提供了一个想法:如果我们想要处理器半负载,我们可以创建一个无限循环,它在一半时间休眠,并占用处理器的另一半。

我将跳过第一次尝试的细节。

第二次尝试

在我的第二次尝试中,一切都很顺利,但仍有一些问题困扰着我

我的第一个解决方案

以下片段是第一个可能的解决方案。它使用GetTickCount()Windows API,并使用两个线程使处理器加载到 50%。

解决方案一要旨

#include <boost/thread.hpp>
#include "windows.h"
#define INTERVAL 10

void infiniteLoop() {
    while (1) {
        DWORD startTime = GetTickCount();
        while (GetTickCount() - startTime <= INTERVAL)
            ; 
        boost::posix_time::millisec sleepTime(INTERVAL);
        boost::this_thread::sleep(sleepTime);
    }
}

int main() {
    boost::thread thread1(infiniteLoop);
    boost::thread thread2(infiniteLoop);
    thread1.join();
    thread2.join();
    char c;
    std::cin >> c;
}

解决方案是成功的,但我不太明白为什么我只能使用两个线程来让 CPU 加载一半,因为 i5-3470 处理器是四核处理器,理论上,我只能使用 25% 加载处理器两个线程。

为什么我使用两个线程而不是四个线程?
一开始我以为处理器是双核处理器,XD。

Q1:这是我的第一个问题:为什么这两个线程infiniteLoop()会消耗 50% 的 qual-core CPU 容量?

我努力解决这个问题,但我真的没有能力自己做......:X

我的第二个解决方案

第二个解决方案与第一个解决方案完全相同,只是我使用clock()fromtime.h来替换GetTickCount()功能。在这个解决方案中,我真的需要 4 个线程才能使处理器加载到 50%。

是代码。

#include <boost/thread.hpp>
#include "windows.h"
#include <ctime>
#define INTERVAL 10

void infiniteLoop() {
    while (1) {
        clock_t startTime = clock();
        while (clock() - startTime <= INTERVAL)
            ;
        boost::posix_time::millisec sleepTime(INTERVAL);
        boost::this_thread::sleep(sleepTime);
    }
}

int main() {
    boost::thread thread1(infiniteLoop);
    boost::thread thread2(infiniteLoop);
    boost::thread thread3(infiniteLoop);
    boost::thread thread4(infiniteLoop);
    thread1.join();
    thread2.join();
    thread3.join();
    thread4.join();
    char c;
    std::cin >> c;
}

这个方案让处理器的总使用率几乎在50%左右,但是通过观察任务管理器->性能->CPU记录,我发现四个核心的使用率并不是均匀分布的,前两个核心的负载几乎是60 %,第三个大约是50%,最后一个只是最大负载的30%左右。

所以这是我的第二个问题。
Q2:为什么这些内核没有均匀加载,这个现象背后是操作系统内部存在某种机制吗?

我的第三个解决方案

另一个想法是完全阻塞两个线程,从而使 CPU 负载为 50%。

这是代码。

#include <boost/thread.hpp>
#include "windows.h"
#include <iostream>

void infiniteRunningLoop() {
    while (1) {
        ;
    }
}

int main() {
  boost::thread thread1(infiniteRunningLoop)
  boost::thread thread2(infiniteRunningLoop)
  thread1.join();
  thread2.join();
}

关于三种解决方案的思考

  • 这三个解决方案不是让 CPU 负载达到 50% 的通用解决方案,因为如果系统中运行其他程序,它们也会消耗 CPU 容量。
  • 该解决方案仅适用于四核处理器,如果要在其他处理器上使用,例如双核处理器,则必须修改线程数。
  • 这三个解决方案都不是优雅的..XD

那么,Q3:有人可以提供一个优雅的解决方案,可以实现目标并且可以在不同类型的处理器之间移植吗?

非常感谢那些阅读问题的人,更感谢那些可能提前回答这个问题的人!

XD

4

3 回答 3

1
  • Q1:看到从使用GetTickCount()clock()解决问题,我想GetTickCount()没有按预期工作,但我真的想不出为什么。
  • Q2:如果线程没有在同一时间开始和结束,则存在重叠,因此可能是,例如,线程 3 在线程 1 等待时开始,它在核心 1 上产生(可用,因为线程 1被暂停)。
  • Q3:我认为正如 Baldrick 所说,获得核心数量并产生正确数量的线程将使您的第二个解决方案非常通用。
于 2013-11-12T14:35:30.450 回答
1

我认为您缺少的关键是计算处理器数量的能力。你可以这样做:

SYSTEM_INFO sysinfo;
GetSystemInfo( &sysinfo );
numCPU = sysinfo.dwNumberOfProcessors;

此计数将与您在任务管理器中看到的数字相同。

如果你将它与你现有的方法结合起来,你应该得到一些非常通用的工作。

于 2013-11-12T14:23:23.727 回答
0

对于 Q1:GetTickCount() 的分辨率似乎大于 10 毫秒。至少在我的基于 i7 860 的 Windows 7 机器上工作。

#include "stdafx.h"
#include <iostream>
#include <windows.h>

int _tmain(int argc, _TCHAR* argv[])
{
    while(1) {
        std::cout <<GetTickCount() << std::endl;
    }
    return 0;
}

一些输出:

510948811 510948811 510948811 510948811 510948811 510948811 510948811 510948811 510948811 510948811 510948811 510948826 510948826 510948826 510948826 510948826 510948826 510948826 510948826 510948826 510948826 510948826 510948826 510948826 510948826 510948826 510948826 510948826 510948826 510948826 510948826 510948826 510948826 510948826 510948826 510948826 510948826 510948826 510948826 510948826 510948826 510948826 510948826 510948826 510948826 510948826 510948842 510948842 510948842 510948842 510948842 510948842

尽管那时我希望您获得 25% 到 50% 的利用率。

编辑:我在 i7 上使用您的 Q1 代码创建了一个项目,将代码调整为 4 个线程,我看到 50% 的利用率。有趣的。

Edit2:如果您将第一个示例代码中的间隔设置为 30,我现在得到预期的行为。

于 2013-11-12T16:24:34.183 回答