在Windows、Linux、Solaris、HP-UX、IBM AIX、Vxworks、Wind River Linux上如何让程序休眠毫秒级别的时间?

11
我必须编写一个C程序,需要在毫秒级别上进行睡眠,可以在Windows、Linux、Solaris、HP-UX、IBM AIX、Vxworks和Windriver Linux等各种平台上运行。
  • Windows上,系统调用Sleep只能按毫秒级别工作。
  • Linux上,sleep只能按秒计算;usleep可以按微秒计算,并且也适用于Solaris。
  • Vxworks中,我希望可以使用taskDelaysysClkRateSet来实现。

我该如何在HP-UX、IBM AIX和Wind River Linux上实现毫秒级别的睡眠?


1
usleep 是 POSIX 规范的,因此应该在所有符合 POSIX 标准的系统(如 HP/UX、AIX 等)中都存在。不过,它已经被标记为过时,推荐使用 nanosleep,而后者则被标记为可选项。 - Some programmer dude
1
请注意,即使您可能找到一种表达 n 毫秒延迟的方法,也绝对不能保证主机操作系统将会 给出 您所要求的确切延迟时间。您列出的操作系统通常不是实时的,因此它们很可能无法保证像这样的事情;当您的延迟结束时,更高优先级的任务可能会占用 CPU。 - unwind
以下链接是否回答了您的问题? https://dev59.com/VHM_5IYBdhLWcg3w8ILR - kunal18
可能是在C中有没有毫秒级别的替代sleep函数?的重复问题。 - Jonathan Leffler
4个回答

16

也许使用特定于平台的#define包装器即可:

#if defined(WIN32)
  #include <windows.h>
#elif defined(__UNIX__)
  #include <unistd.h>
#else
#endif

...

int millisleep(unsigned ms)
{
#if defined(WIN32)
  SetLastError(0);
  Sleep(ms);
  return GetLastError() ?-1 :0;
#elif defined(LINUX)
  return usleep(1000 * ms);
#else
#error ("no milli sleep available for platform")
  return -1;
#endif
}

更新

参考下面Jonathan的评论,请在此处找到一个更现代、更便携(并且也更正:))的版本:

#if defined(WIN32)
  #include <windows.h>
#elif defined(__unix__)
  #include <time.h>
  #include <unistd.h>
#else
#endif

...

int millisleep(unsigned ms)
{
#if defined(WIN32)

  SetLastError(0);
  Sleep(ms);
  return GetLastError() ?-1 :0;

#elif _POSIX_C_SOURCE >= 199309L

  /* prefer to use nanosleep() */

  const struct timespec ts = {
    ms / 1000, /* seconds */
    (ms % 1000) * 1000 * 1000 /* nano seconds */
  };

  return nanosleep(&ts, NULL);

#elif _BSD_SOURCE || \
  (_XOPEN_SOURCE >= 500 || \
     _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED) && \
  !(_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)

  /* else fallback to obsolte usleep() */

  return usleep(1000 * ms);

#else

# error ("No millisecond sleep available for this platform!")
  return -1;

#endif
}

在POSIX 2004中,usleep() 被标记为“过时”,并在POSIX 2008中被删除。首选替代方法是 nanosleep()。POSIX的一般趋势是更喜欢具有纳秒分辨率的 struct timespec,而不是仅具有微秒分辨率的 struct timeval 或使用微秒分辨率但不使用结构的 usleep() - Jonathan Leffler
@JonathanLeffler:谢谢通知。已更正。 - alk

1
我注意到 usleep 已经不建议使用了,但它比 nanosleep 简单得多。所以,在调试脚本时需要进行从秒到毫秒或零的轻松调整的增强睡眠时,我选择使用它。
这个 snooze 函数结合了 sleep 和 usleep 的优点,因此您可以输入一个整数或浮点数以获取所需的延迟时间,0.1 将睡眠 0.1 秒,而 3 将睡眠 3 秒。 3.5 秒将被视为 3 秒。
在 Linux Mint 18.3 (Ubuntu 16.04.9) 上作为 C 和 C++ 进行测试,使用 gcc 5.4.0。
#include <unistd.h>

void snooze(double t) {(t > 1.0) ? sleep(t) : usleep(t*1000000);}

snooze(0.01);  // call function to sleep for 10ms



为了完整性,这是一个nanosleep版本。它可能比usleep版本更准确,并且不会受过时的威胁。

#include <time.h>
#include <math.h>

void snooze(double t) {
    struct timespec req = {t, fmod(t, 1.0) * 1E9};
    nanosleep(&req, NULL);
}

// struct timespec req = {t, fmod(t, 1.0) * 1E9};
//  is equivalent to:
// struct timespec req = {0};
// req.tv_sec = t;
// req.tv_nsec = fmod(t, 1.0) * 1000000000L;

// NULL as value for *rem so no resumption after signal interrupts

snooze(1.99);  // call for delay of 1.99 seconds

正如@alk所建议的那样,以下版本将返回被调用的 sleep 函数的错误(如果发生错误)或 0(如果成功)。定义结构 rem(aining) 也允许在信号中断后恢复。

int snooze(double t) {
    return (t > 1.0) ? sleep(t) : usleep(t*1000000);
}

int snooze(double t) {
    struct timespec req = {t, fmod(t, 1.0) * 1E9};
    struct timespec rem = {0, 0.0};
    return nanosleep(&req, &rem);
}

为了完整性,您可能需要注意所使用的函数返回的值。 - alk
感谢您的建议alk。我已经添加了选项。 - John 9631

1
考虑使用空的FD集合和所需的超时时间来执行select操作。根据man select

有些代码在所有三个集合都为空,nfds为零,并且timeout非空的情况下调用select()函数,这种方法可以实现以亚秒精度的方式进行睡眠,而且相当可移植。

实际上,对于任何非Windows系统,这可能是最佳解决方案。


感谢您的回复。目前我们只使用这个,但在某些平台上,如Windriver Linux,select是不可用的。 - rashok
@rashok 在 HP-UX 和 AIX 上存在 select 函数。我对它在 Wind River Linux 上不存在的说法感到惊讶。Wind River Linux 是 Linux,那么它怎么可能缺少 select 系统调用(或其更新的替代品 newselect 或 pselect6)呢? - Simon Kissane

0
如果在Windows中安装了PHP。
这种方法有效: php -r "usleep(200000);"
将PHP二进制文件设置为全局PATH。

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