在Windows上我遇到了一个我从未在Unix上遇到过的问题.这是如何使线程休眠不到一毫秒.在Unix上,您通常有许多选择(睡眠,睡眠和纳米睡眠)以满足您的需求.但是,在Windows上,只有Sleep具有毫秒级的粒度.
在Unix上,我可以使用select
系统调用来创建一个非常简单的微秒睡眠:
int usleep(long usec) { struct timeval tv; tv.tv_sec = usec/1000000L; tv.tv_usec = usec%1000000L; return select(0, 0, 0, 0, &tv); }
如何在Windows上实现相同的目标?
这表明对睡眠功能的误解.您传递的参数是睡眠的最短时间.无法保证线程将在指定的时间后唤醒.实际上,线程根本不会"唤醒",而是被调度程序选择执行.调度程序可能会选择等待比请求的睡眠持续时间更长的时间来激活线程,尤其是当另一个线程在此时仍处于活动状态时.
正如乔尔所说,你不能在如此短的时间内有意义地"睡觉"(即放弃你的预定CPU).如果你想延迟很短的时间,那么你需要旋转,反复检查一个适当的高分辨率计时器(例如'性能计时器'),并希望高优先级的东西不会先发制人.
如果你真的关心如此短暂的准确延迟,你不应该使用Windows.
使用winmm.lib中提供的高分辨率计时器.请参阅此示例.
#includestatic NTSTATUS(__stdcall *NtDelayExecution)(BOOL Alertable, PLARGE_INTEGER DelayInterval) = (NTSTATUS(__stdcall*)(BOOL, PLARGE_INTEGER)) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtDelayExecution"); static NTSTATUS(__stdcall *ZwSetTimerResolution)(IN ULONG RequestedResolution, IN BOOLEAN Set, OUT PULONG ActualResolution) = (NTSTATUS(__stdcall*)(ULONG, BOOLEAN, PULONG)) GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwSetTimerResolution"); static void SleepShort(float milliseconds) { static bool once = true; if (once) { ULONG actualResolution; ZwSetTimerResolution(1, true, &actualResolution); once = false; } LARGE_INTEGER interval; interval.QuadPart = -1 * (int)(milliseconds * 10000.0f); NtDelayExecution(false, &interval); }
是的它使用了一些未记录的内核函数,但它运行得很好,我使用SleepShort(0.5); 在我的一些threds
是的,您需要了解您的操作系统的时间量.在Windows上,除非将时间量更改为1毫秒,否则您甚至不会获得1毫秒的分辨率.(例如使用timeBeginPeriod()/ timeEndPeriod())这仍然不能保证任何东西.即使是一点负载或一个蹩脚的设备驱动程序也会抛弃一切.
SetThreadPriority()有帮助,但非常危险.糟糕的设备驱动程序仍然会毁了你.
你需要一个超控制的计算环境来让这些丑陋的东西工作.
如果你想要这么多粒度,你就在错误的地方(用户空间).
请记住,如果您在用户空间,您的时间并不总是精确的.
调度程序可以启动您的线程(或应用程序),并安排它,因此您依赖于OS调度程序.
如果您正在寻找精确的东西,您必须:1)在内核空间(如驱动程序)2)选择RTOS.
无论如何,如果您正在寻找一些粒度(但请记住用户空间的问题),请查看MSDN中的QueryPerformanceCounter函数和QueryPerformanceFrequency函数.
正如几位人士指出的那样,睡眠和其他相关功能默认依赖于"系统节拍".这是OS任务之间的最小时间单位; 例如,调度程序的运行速度不会比这更快.即使使用实时操作系统,系统滴答通常也不会小于1毫秒.虽然它是可调的,但这会影响整个系统,而不仅仅是睡眠功能,因为您的调度程序将更频繁地运行,并可能增加操作系统的开销(调度程序运行的时间,相对于任务可以运行的时间).
解决方案是使用外部高速时钟设备.大多数Unix系统都允许您指定定时器和使用的不同时钟,而不是默认的系统时钟.
通常,睡眠将至少持续到下一个系统中断发生为止。但是,这取决于多媒体计时器资源的设置。可能将其设置为接近1毫秒,某些硬件甚至允许以0.9765625的中断周期运行(所提供的NtQueryTimerResolution
ActualResolution将显示0.9766,但这实际上是错误的。他们只是无法将正确的数字输入ActualResolution格式。它为0.9765625毫秒,每秒1024次中断)。
有一个例外可以使我们摆脱以下事实:睡眠时间少于中断时间是不可能的:这是著名的Sleep(0)
。这是一个非常强大的工具,使用频率不高!它放弃了线程时间片的提醒。这样,线程将停止,直到调度程序迫使该线程再次获得cpu服务为止。Sleep(0)
如果是异步服务,则该调用将强制调度程序独立于中断做出反应。
第二种方法是使用waitable object
。诸如等待功能的等待功能WaitForSingleObject()
可以等待事件。为了使线程在任何时间(也包括微秒)内处于休眠状态,该线程需要设置一些服务线程,该服务线程将以所需的延迟生成事件。“正在休眠”线程将设置此线程,然后在等待功能处暂停,直到服务线程将设置发出信号的事件为止。
这样,任何线程都可以“休眠”或等待任何时间。服务线程可能非常复杂,并且可能提供系统范围的服务,例如以微秒为单位的定时事件。但是,微秒分辨率可能会迫使服务线程在高分辨率时间服务上旋转最多一个中断周期(〜1ms)。如果注意,它可以很好地运行,尤其是在多处理器或多核系统上。如果仔细处理了调用线程和服务线程的相似性掩码,则在多核系统上旋转1 ms不会造成很大的伤害。
可以在Windows时间戳项目中访问代码,描述和测试。