sleep 和 usleep 的理念是,让 CPU 运行一些空闲周期,以便其他程序可以运行一些自己的周期。这会导致更好的响应时间和更低的整体系统负载。因此,如果您必须等待某些东西,请休眠几秒钟,而不是在不做任何事情的情况下占用 CPU。
(PHP 4, PHP 5, PHP 7, PHP 8)
usleep — 以微秒为单位延迟执行
microseconds
暂停时间(以微秒为单位)。1 微秒等于 1 秒的百万分之一。
注意: 大于
1000000
的值(即休眠超过一秒)可能不受操作系统支持。请使用 sleep() 代替。
注意: 休眠时间可能会略微延长(即可能超过
microseconds
),这是由任何系统活动、处理调用所花费的时间或系统计时器的粒度造成的。
不返回任何值。
示例 #1 usleep() 示例
<?php
// 当前时间
echo (new DateTime('now'))->format('H:i:s.v'), "\n";
// 等待 2 毫秒
usleep(2000);
// 返回!
echo (new DateTime('now'))->format('H:i:s.v'), "\n";
// 等待 30 毫秒
usleep(30000);
// 再次返回!
echo (new DateTime('now'))->format('H:i:s.v'), "\n";
?>
上面的示例将输出
11:13:28.005 11:13:28.007 11:13:28.037
sleep 和 usleep 的理念是,让 CPU 运行一些空闲周期,以便其他程序可以运行一些自己的周期。这会导致更好的响应时间和更低的整体系统负载。因此,如果您必须等待某些东西,请休眠几秒钟,而不是在不做任何事情的情况下占用 CPU。
在 MacOS X 和 Linux 上,usleep() 调用似乎消耗了 CPU 周期,而 sleep() 和 time_nanosleep() 则没有。这在 PHP 5.3.29 和 5.5.29 上是一样的。
我使用了一个循环,其中只包含对 sleep/usleep/time_nanosleep 的调用,并将它们全部与一个空循环进行了比较。显然,空循环消耗了 99% 的 CPU,sleep 使用了 0%,usleep 在 1000ms 时使用了 3%,在 100ms 时使用了 6%,time_nanosleep 在 500ms 和 1000ms 时都使用了 0%。
应该注意的是,Windows 机器在使用 kernel32.dll 中的 Sleep() 函数时,分辨率为 10 毫秒或 15 毫秒(取决于芯片组实现和使用的 HAL)。这意味着您的平均误差将为 5 毫秒或 7.5 毫秒。这通常不是问题,除非您确实需要休眠的时间小于 Windows 提供的粒度。
应该注意的是,循环非常快以创建延迟的函数也会在执行循环时消耗 100% 的 CPU。尝试创建一个循环 100000 次的虚拟循环,看看它会如何使您的机器窒息。如果您确实需要 usleep(),请不要使用 Windows。
如果您使用的是 Windows,那么如果您确实需要使用 usleep,您可能会遇到麻烦。
使用 fsockopen 的 Bernie 的微延迟函数不能正常工作,而 fclose 也没有多大帮助。
我不知道网络连接是否变得奇怪,但我知道它不起作用,因为您已经对它进行了超过 2000-3000 次调用,因此它不是“长时间运行”的 PHP 脚本中的可靠解决方案,或者这是我的 PHP 和 PHP-GTK 应用程序中微延迟函数的问题。
虽然应该找到另一种解决方案,但我在谷歌搜索了一段时间,发现了一个 WinAPI 函数:Sleep。
因此,我使用这段代码片段,它对我来说运行良好,您可以获得毫秒级精度,但更重要的是,它适用于长时间运行的脚本,当然,它不会浪费任何 CPU 周期。
dl('php_w32api.dll');
$GLOBALS['win32api'] =& new win32;
// Windows 和 PHP4 的 USleep 替代方案
$GLOBALS['win32api']->registerfunction("long Sleep (long dwMillisecods) From kernel32.dll");
// 现在您可以从脚本中的任何地方调用该函数:$GLOBALS['win32api']->Sleep(milliseconds);
for ($msec = 2000; $msec > 0; $msec = $msec - 125) {
echo "Hi. Next one in $msec msec.\n";
$GLOBALS['win32api']->Sleep($msec);
}
关于使用 fsockopen 发布的 microdelay() 代码,需要提醒您一点——如果您在循环中使用它来延迟一小段时间,您将很快耗尽套接字/套接字缓冲区空间。然后您的网络连接会变得非常奇怪......
我花了几天时间试图为 Windows 创建一个可靠的 usleep() 替代方案。
我只能提供这些
正如其他人已经评论的那样,下面使用的 gettimeofday() 方法毫无用处——PHP 会使用所有可用的 CPU 能力,却什么也不做。
fsockopen() 方法显然也是毫无用处的——正如其他人评论的那样,原始帖子中缺少一个 fclose(),但这显然并没有解决问题。在调用该函数大约 50 次后,fsockopen() 会立即返回,没有任何延迟——并且在 Windows 中观察进程监视器时,您可以看到该进程占用越来越多的内存,直到最终 PHP 达到最大值时中止(或崩溃)。
win32api 方法也是不可行的……在调用 Sleep 函数数百次后(在此期间,内存使用量也会由于某个地方的内存泄漏而每次都增加),PHP 会引发异常,Windows 会终止它。
我已经放弃了——我认为在 PHP 4 下没有可行的解决方案来解决这个问题。
如果您需要此函数,请将您的项目升级到 PHP 5。
或者使用 sleep() 函数进行 1 秒的延迟。
不幸的是,这些似乎是您的唯一选择……
兄弟,你真是这代码片段的救星。它完美地解决了问题。我只想指出一些事情,并提供我自己的改进。
1. 如果你像我一样,你可能想知道为什么套接字必须在每次调用时重新创建,以及为什么不能只创建一个静态套接字。这是因为 socket_select 假设你传递的是一个指针,并且会在返回时改变变量以反映实际更改的套接字。
2. 我百思不得其解,为什么 socket_select 没有定义。因为你在 php.ini 中没有启用正确的扩展。
好吧,这是我的微小改进。我真正做的只是为套接字使用一个静态变量,以避免在每次调用此函数时创建一个全新的套接字。我不确定套接字创建是否会导致像这里报道的其他问题那样导致系统崩溃,但如果你问我,预防胜于治疗。
function Sleeper($mSec)
{
// 为了像我一样花了 5 分钟
// 思考为什么 socket_create 没有定义
if(!function_exists('socket_create')){
die("请启用扩展 php_sockets.dll");
}
// 这样套接字只创建一次
static $socket=false;
if($socket===false){
$socket=array(socket_create(AF_INET,SOCK_RAW,0));
}
$pSock=$socket;
// 计算时间
$uSex = $mSec * 1000;
// 执行等待
socket_select($read=NULL,$write=NULL,$pSock,0,$uSex);
// 强迫症
return true;
}
请注意,此函数存在开销!
示例
<?php
for ($i = 0; $i < 1000000; ++$i)
{
usleep(1);
}
?>
此代码块在我的服务器上运行约 70 秒。
脚本每次调用 usleep() 函数大约花费 70 微秒。
要监控脚本的 CPU 使用率并避免任何讨厌的 CPU 吞噬循环,你可以使用此函数(不适用于 Windows 或安全模式)我知道它在 FreeBSD 上有效
function phpmon($max)
{
$cmd = `ps -Unobody -r -o%cpu`;
$lines = explode("\n", $cmd);
$usage = substr($lines[1], 0, strpos($lines[1], "."));
$sleeprate = 500;
while ($usage >= $max)
{
$cmd = `ps -Unobody -r -o%cpu`;
$lines = explode("\n", $cmd);
$usage = substr($lines[1], 0, strpos($lines[1], "."));
usleep($sleeprate);
}
}
phpmon($MAX);
其中 $MAX 是你想让进程消耗的最大 CPU 使用率。如有任何改进/建议,请发邮件给我。
我注意到这会消耗大量的系统 CPU(至少在我有限的测试中),可能是由于所有的系统调用或我用来测试脚本有效性的庞大数学函数。
我不知道为什么没有人想到这一点,但有一种在 Windows 下复制 usleep() 的有效方法。
<?php
function usleep_win($msec) {
$usec = $msec * 1000;
socket_select($read = NULL, $write = NULL, $sock = array(socket_create (AF_INET, SOCK_RAW, 0)), 0, $usec);
}
?>
* 不进行繁忙等待
* 不占用内存
* 适用于数百万次重复
* 似乎相当高效
似乎平均误差(在我的机器上)约为 5 毫秒(它比预期多休眠 5 毫秒),这可能是由于代码执行以及内核计时器造成的。
没有目的的套接字不太可能引起异常,因此 socket_select 始终会休眠,直到超时为止。
我想创建一个守护进程/Linux 服务。以下是如何运行具有“节流控制”的进程的示例。
// 你必须设置这些
//
// max_execution_time = 0
// max_input_time = 0
function doProcess() {
echo "Start"."\n";
usleep(10000);
echo "Stop"."\n";
return false;
}
function manageProcess() {
// 设置数据
$runsPerMinute = 200;
$maxMinuteAverage = 5;
$waitIfNotWorking = 120; // 秒
// 转换
$microsPerSecond = 1000000;
// 统计信息
$currentMinute = 0;
$minute = -1;
$countPerMinute = array();
$sumPerMinute = array();
// 总计
$totalProcessTime = 0;
$totalCounts = 0;
while (true) {
$timestart = microtime();
$performedWork = doProcess();
$timeend = microtime();
if (!$performedWork) {
// 统计信息
$currentMinute = 0;
$minute = -1;
$countPerMinute = array();
$sumPerMinute = array();
sleep($waitIfNotWorking);
} else {
$ts = split(" ",$timestart);
$te = split(" ",$timeend);
$te[0] = ($te[0] * $microsPerSecond) - ($ts[0] * $microsPerSecond);
$te[1] = ($te[1] - $ts[1]) * $microsPerSecond;
$processTime = $te[0] + $te[1];
if (date("i")<>$minute) { // 我们不在同一分钟内
// 重置新分钟
$minute = date("i");
$currentMinute = ($currentMinute+1) % $maxMinuteAverage;
// 从我们要过期的分钟中删除统计信息。
if (isset($countPerMinute[$currentMinute])) {
$totalProcessTime = $totalProcessTime - $sumPerMinute[$currentMinute];
$totalCounts = $totalCounts - $countPerMinute[$currentMinute];
}
$countPerMinute[$currentMinute] = 0;
$sumPerMinute[$currentMinute] = 0;
}
$countPerMinute[$currentMinute] = $countPerMinute[$currentMinute] + 1;
$sumPerMinute[$currentMinute] = $sumPerMinute[$currentMinute] + $processTime;
$totalCounts = $totalCounts + 1;
$totalProcessTime = $totalProcessTime + $processTime;
$averageRuntime = round($totalProcessTime / $totalCounts);
$waitTime = (($microsPerSecond*60) / $runsPerMinute) - $averageRuntime;
usleep($waitTime);
}
}
}
manageProcess();