减缓线程的最佳方法是什么?使用Sleep()可以吗?

8

我编写了一个C++库,执行一些严重的CPU工作(所有工作都是数学和计算),如果让它自己运行,将轻松消耗所有可用的CPU资源(它也是多线程到机器上可用的逻辑核心数量)。

因此,我在主计算循环中设置了一个回调函数,使用库的软件应该调用这个函数:

while(true)
{
    //do math here
    callback(percent_complete);
}

在回调函数中,客户端调用Sleep(x)来减缓线程速度。
最初,客户端代码是一个固定的Sleep(100)调用,但这会导致性能不稳定,因为有些机器比其他机器更快地完成计算,但所有机器上的睡眠时间相同。所以现在客户端检查系统时间,如果超过1秒钟(即几次迭代),它将睡眠半秒钟。
这种减慢线程速度的方式可行吗?我应该使用信号量/互斥锁来代替Sleep()以最大化性能吗?每处理1秒的工作就睡眠x毫秒是否可以?还是我没有注意到什么问题?
我问这个问题的原因是,尽管任务管理器显示进程占用了约10%的CPU,但机器仍然严重卡顿。我已经探索了硬盘和内存争用,但无济于事,现在我想知道我减慢线程速度的方式是否导致了这个问题。
谢谢!
5个回答

23

你为什么不为计算线程使用较低的优先级?这将确保其他线程在需要运行时得到调度,同时允许你的计算线程在没有其他线程需要运行时以最快速度运行。


4
实际应用中,在Windows上这并不是很可靠。 我将库移植到了OS X上,那里它的优先级较低却能够完美运行,但在Windows上仍会导致问题。 另外还有一个问题:为了营销和技术支持的目的,我们认为软件占用CPU超过20%就不是个好主意,否则你会收到一些关于你的软件拖累PC性能的抱怨和荒谬的评论。人们看到50%的CPU使用率就会恐慌! - Mahmoud Al-Qudsi
1
我不能说为什么你的端口会表现得那样。根据我的经验,调度在 Windows 上运行良好。无论如何,考虑到问题,我认为最好使用优先级解决,并将调度留给操作系统处理。 - Brian Rasmussen
11
计算机专家,线程优先级在Windows上非常可靠。然而,如果你正在进行大量的I/O操作(无论是直接还是由内存分页引起的),你可能也需要降低I/O优先级。请参考SetThreadPriority和THREAD_MODE_BACKGROUND_BEGIN。 - avakar
1
谢谢,avakar。我没有想到那个 - 确实涉及了相当多的文件读写(尽管现在已经缓冲为4MB块,我不确定在我第一次尝试降低线程优先级时是否是这样),还有大量的内存分页(这本身就是另一个问题)。我会试一试 :) - Mahmoud Al-Qudsi
1
一个可能的区别是,Mac 硬件的最大性能更加优雅,而许多人无法忍受风扇全速运转时长时间的坐姿。 - Potatoswatter

4
CPU使用率达到100%有什么问题?这正是你应该追求的,而不是试图避免。这些数学计算很重要,对吧?除非你试图避免占用某些操作系统没有明确管理但主线程正在使用的其他资源(如互斥量、磁盘等),通常试图减缓线程速度都是不好的主意。那么多核系统呢(几乎所有系统将来都会是)?你会毫无理由地减慢一个线程的速度。
操作系统具有线程时间片的概念。它会确保您的系统上没有重要的线程被饿死。并且,就像我之前提到的那样,在多核系统上,一个CPU上的一个线程不会对其他核上的其他线程造成性能损失。
我还看到在另一条评论中,这个线程还在进行大量的磁盘I/O——这些操作将已经导致您的线程在等待结果时屈服,因此睡眠将无效。
总的来说,如果你在调用Sleep(x),那么你的设计可能存在问题或者是有点懒惰,如果x==0,那么你自己就会置身于活锁的风险之中(调用Sleep(0)的线程实际上可以立即重新调度,从而使它成为no-op)。

也许稍微解释一下背景会有所帮助:这个数学计算是为了备份程序计算两个文件之间的最小差异。一个“设置并忘记”的备份程序不应该占用100%的CPU,而一个单一目的的“文件差异”程序则应该占用。这就是这个设计的关键 :) - Mahmoud Al-Qudsi
1
啊,我明白了;这正是进程和IO优先级被发明的原因。你应该这样做,让操作系统调度程序来处理事情。跳过Sleeps。 - Terry Mahaffey
3
我认为Computer Guru的意思是他的备份程序不应该占用100%的CPU,即使系统完全空闲。karunski说Linux通过分配足够低的优先级来实现这一点,但这并不是POSIX中定义的优先级的含义。在进程之间存在相对优先级差异(“如果会导致某些重要的东西受到影响就不要快速执行”),以及我猜你可以称之为绝对优先级的差异(“永远不要快速执行”)。 - Steve Jessop
我不相信你的“如果它是唯一的活动任务,让操作系统将你的进程置于100%应始终被视为好”的规则。因为会导致加热问题、风扇噪音、如果系统散热能力差可能会出现降频的风险,更不用提守护程序的不良判断了。后台进程应该是低调的。 - v.oddou

2
“睡眠”功能对于限制应用程序来说应该是有效的,根据您的评论,这正是您想要的。也许您只需要更精确地设置睡眠时间。
我唯一使用此类功能的软件是 BOINC客户端。我不知道它使用了什么机制,但它是开源的,支持多平台,所以你可以自行查看。
它有一个配置选项(“将CPU使用限制为X%”)。我期望实现的方式是使用依赖于平台的API(如clock()GetSystemTimes()),并将处理器时间与经过的墙钟时间进行比较。做一些真正的工作,检查是否超过了或低于标准值,如果超过了标准值,就睡一会儿以回到正常范围。
BOINC客户端与优先级相容,并且即使在100%最大CPU时也不会影响其他应用程序的性能。我使用限制是因为否则,客户端会一直以最大速度运行CPU,导致风扇转速和噪音增加。因此,我将其保持在使风扇保持安静的水平。如果有更好的冷却可能就不需要它了 :-)

在Linux上,如果您使用更高的nice级别运行进程,则操作系统将保持CPU时钟低,假设您的硬件支持CPU速度缩放。这将保持功耗、热量和噪音低,而不需要人为地限制性能。 - karunski
很好。完全转向Linux是我经常考虑但迄今为止总是拒绝的选择... - Steve Jessop

0

看一下cpulimit。它会发送SIGSTOPSIGCONT,以保持进程在给定的CPU使用率以下。

即使如此,“关于你的软件导致PC性能下降的疯狂投诉和荒谬评论”也让人困惑。我更可能抱怨你的软件运行缓慢,没有充分利用我的硬件,但我不是你的客户。

编辑:在Windows上,SuspendThread()ResumeThread()可能会产生类似的行为。


0

另一种不那么复杂的方法是计时一个迭代,然后让线程在下一次迭代之前睡眠(x * t)毫秒,其中t是一个迭代的毫秒时间,x是选择的睡眠时间分数(介于0和1之间)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接