C主循环避免100%的CPU占用

21
#include <stdio.h>

int main() {
  while(!DONE) {
    /* check for stuff */
  }
  return 0;
}

以上代码示例会一直占用100%的CPU,直到DONE为真。我该如何实现一个程序循环,只有在DONE时终止,但不会使用100%的CPU?现代语言使用类似于App.ProcessMessages之类的东西将控制权交给操作系统,并返回到循环中。

我是C语言新手... 使用最新的GCC,Linux和Windows(可移植解决方案最好)。


8
你应该使用同步对象,这是一个可以等待直到它变为已通知状态的对象,这样你就不会占用 CPU 资源。 - Lasse V. Karlsen
2
DONE是什么,什么会导致它变为非零值?大概一直处理到“检查东西”的评估出现将DONE设置为非零的情况才有意义吧? - CB Bailey
Lasse V. Karlsen: 你的解决方案似乎很有道理(它可能确实很有道理,但我没有那个知识)。你能详细说明一下吗?我应该调查线程吗?(举个例子会很好)Charles Bailey: DONE 应该是一个 int(而不是 DONE),当用户选择退出应用程序时,它将被设置为 1,例如。 如果我表达得不够清楚,我很抱歉。 - pwseo
4
单线程应用程序不需要使用任何与线程相关的内容来避免繁忙等待。您能否告诉我们(甚至是一般地)循环中的代码正在做什么?您似乎在暗示它将接受用户输入。这本身就会导致它花费大量时间在等待中,而不会消耗CPU。 - Tyler McHenry
1
如果“用户输入”是您正在等待的唯一项目,则应在您使用的任何用户输入方法上使用阻塞读取。在获得详细的程序信息之前,您需要提供更多的详细信息才能得到有用的答案。特别是,如果您只需要像getchar()这样的阻塞调用,则线程和同步对象会更加复杂。 - CB Bailey
显示剩余3条评论
11个回答

15

这取决于您在此循环内要执行的操作。

如果您在循环内等待(例如,如果按键后执行某些操作),那么您的机制将浪费系统资源而没有任何返回。更快的处理器只会导致更多的空闲循环。可以通过等待事件来解决此问题,而不仅仅是睡眠,最好是触发可执行有意义操作的事件。例如,文件操作(stdin也是文件)是一种便携式机制。这将为其他应用程序让路,直到数据可用。当您变得更具体时,可能需要深入研究信号量或信号,这通常取决于操作系统。抽象层可以解决此问题。

如果您正在执行某些有用的操作(例如处理大量数据),那么100%的CPU负载意味着处理器以最高效的方式使用。您可以依靠操作系统让出其他可能优先级更高的任务。

使用像sleep这样的函数将降低CPU使用率,但是您的应用程序将变慢。这将需要在可接受的性能和CPU负载之间进行权衡。最大执行速度将由您的睡眠参数定义,而不再由CPU速度定义。此外,如果电力是一个问题(例如电池寿命),那么这将导致CPU在没有工作要做的情况下唤醒(睡眠期结束);即系统资源的不同浪费。


我猜我得学习信号量和信号了。有任何具体的指针吗? :) - pwseo
2
主循环内有哪些处理?消息、信号、信号量更多地与操作系统相关,而不是底层语言。例如,http://www.ibm.com/developerworks/eserver/library/es-win32linux-sem.html 显示了在 Windows 和 Linux 中如何处理信号量。如果您需要支持两者,可以使用 POSIX(对于 Windows 可选,即在 cygwin 中)或将代码放入平台相关模块中,为每个操作系统创建一个模块。 - Adriaan

12

您有几个选择:

  1. 使用sleep()定期强制挂起进程,让其他进程使用CPU
  2. 以较低的优先级运行-这将导致操作系统分配更少的CPU时间
  3. 使用互斥或其他同步对象检测何时有可用的工作-这将使进程在没有实际工作时不会消耗任何CPU时间
  4. 如果您的工作速度比处理速度快-您可能仍然需要使用一些睡眠/优先级模型来避免完全消耗CPU。

选项#2在平台/操作系统中实现起来可能有些棘手。最好的方法是启动进程并在运行时环境中更改其优先级。


7

您有两个选项:轮询和某种事件通知。

轮询是最容易编程的方式 - 基本上,每次循环通过时,您的线程都会休眠一小段时间。这将释放处理器以执行其他任务。缺点是在“检查是否有东西”的代码中会有延迟 - 因此,如果您休眠一秒钟,则可能需要一秒钟才能检测到条件。您的代码在此处易于移植。

另一个选择是等待 POSIX 条件、Windows 事件或类似物。不仅会更改此代码,而且“您正在检查的内容”还需要触发标志以表示其已完成。这将是稍微不太可移植的代码,尽管可能有库来抽象平台。但您将立即获得事件结果,并且不会浪费处理器时间检查不存在的事物。


3

你具体在检查什么?

如果你正在检查一些易受硬件或其他进程更改的不稳定内容,只需在循环中调用sleep

如果你正在等待文件描述符或网络套接字描述符,则需要在循环中使用selectpoll等待描述符准备好数据以供使用。


2

Sleep(0); 我想应该已经足够了。


2
如果我理解正确,您在评论中提到DONE可以从其他线程更改。如果是这样,条件变量就有意义了。使用pthread,可以这样做:
在等待的线程中:
pthread_mutex_lock(&mutex);
while (!DONE) {
     pthread_cond_wait(&cond, &mutex);
}
pthread_mutex_unlock(&mutex);

在其他线程中,当“DONE”发生改变时:
pthread_mutex_lock(&mutex);
DONE = 1;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);

1

使用

Sleep(int 毫秒)


3
睡眠函数的参数实际上是秒数,而不是毫秒数。 - Tyler McHenry
1
我认为这实际上取决于操作系统...虽然在Unix/Linux/*BSD下参数确实是秒,但我记得在Windows下它是毫秒...不过那是几年前的事了,所以我可能记错了... - Nicolas
5
sleep()函数接受以秒为单位的时间。usleep()函数(BSD和POSIX系统都支持)接受以微秒为单位的时间。nanosleep()函数(同样在POSIX系统中使用)接受以纳秒为单位的时间。 - qrdl
1
睡眠应该是最后的选择,因为即使没有工作要做,它也会唤醒处理器。这可能会破坏笔记本电脑等设备的电源管理。 - Ron Warholic

0

这不是可移植的,因为它既没有被C标准定义,也没有被POSIX所规范。 - Tyler McHenry
1
这难道不会只是给其他进程更多的时间,仍然占用其他进程没有消耗的所有时间吗? - liori

0
如果我猜得没错的话(我不确定),App.ProcessMessages的等效操作是阻塞IO。由于我不知道任何在多任务操作系统上使用轮询的C实现,因此任何标准的C IO都应该是安全的。

0
在Windows上,您可以使用定义在windows.h中的Sleep(int毫秒)。

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