C++ 如何在 Windows 上使线程休眠少于一毫秒
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/85122/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to make thread sleep less than a millisecond on Windows
提问by Jorge Ferreira
On Windows I have a problem I never encountered on Unix. That is how to get a thread to sleep for less than one millisecond. On Unix you typically have a number of choices (sleep, usleep and nanosleep) to fit your needs. On Windows, however, there is only Sleepwith millisecond granularity.
在 Windows 上,我遇到了在 Unix 上从未遇到过的问题。这就是让线程休眠少于一毫秒的方法。在 Unix 上,您通常有多种选择(sleep、usleep 和 nanosleep)来满足您的需求。然而,在 Windows 上,只有毫秒级的睡眠。
On Unix, I can use the use the select
system call to create a microsecond sleep which is pretty straightforward:
在 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);
}
How can I achieve the same on Windows?
如何在 Windows 上实现相同的目标?
采纳答案by Jorge Ferreira
On Windows the use of select
forces you to include the Winsocklibrary which has to be initialized like this in your application:
在 Windows 上,使用select
强制您包含Winsock库,该库必须在您的应用程序中像这样初始化:
WORD wVersionRequested = MAKEWORD(1,0);
WSADATA wsaData;
WSAStartup(wVersionRequested, &wsaData);
And then the select won't allow you to be called without any socket so you have to do a little more to create a microsleep method:
然后 select 将不允许在没有任何套接字的情况下调用您,因此您必须做更多的事情来创建 microsleep 方法:
int usleep(long usec)
{
struct timeval tv;
fd_set dummy;
SOCKET s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
FD_ZERO(&dummy);
FD_SET(s, &dummy);
tv.tv_sec = usec/1000000L;
tv.tv_usec = usec%1000000L;
return select(0, 0, 0, &dummy, &tv);
}
All these created usleep methods return zero when successful and non-zero for errors.
所有这些创建的 usleep 方法在成功时返回零,错误时返回非零。
回答by Joel Coehoorn
This indicates a mis-understanding of sleep functions. The parameter you pass is a minimumtime for sleeping. There's no guarantee that the thread will wake up after exactly the time specified. In fact, threads don't "wake up" at all, but are rather chosen for execution by the OS scheduler. The scheduler might choose to wait much longer than the requested sleep duration to activate a thread, especially if another thread is still active at that moment.
这表明对睡眠功能的误解。您传递的参数是睡眠的最短时间。不能保证线程会在指定的时间之后唤醒。事实上,线程根本不会“唤醒”,而是由操作系统调度程序选择执行。调度程序可能会选择等待比请求的睡眠持续时间长得多的时间来激活一个线程,尤其是当另一个线程此时仍处于活动状态时。
回答by Will Dean
As Joel says, you can't meaningfully 'sleep' (i.e. relinquish your scheduled CPU) for such short periods. If you want to delay for some short time, then you need to spin, repeatedly checking a suitably high-resolution timer (e.g. the 'performance timer') and hoping that something of high priority doesn't pre-empt you anyway.
正如乔尔所说,你不能在这么短的时间内有意义地“睡眠”(即放弃你预定的 CPU)。如果你想延迟一段时间,那么你需要旋转,反复检查一个合适的高分辨率计时器(例如“性能计时器”),并希望高优先级的东西无论如何都不会抢占你。
If you really care about accurate delays of such short times, you should not be using Windows.
如果您真的关心如此短时间的准确延迟,那么您不应该使用 Windows。
回答by Joe Schneider
回答by Oskar Dahlberg
#include <Windows.h>
static 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);
}
Works very well for sleeping extremely short times. Remember though that at a certain point the actual delays will never be consistent because the system can't maintain consistent delays of such a short time.
非常适合睡眠极短的时间。请记住,在某个时刻,实际延迟永远不会一致,因为系统无法在如此短的时间内保持一致的延迟。
回答by darron
Yes, you need to understand your OS' time quantums. On Windows, you won't even be getting 1ms resolution times unless you change the time quantum to 1ms. (Using for example timeBeginPeriod()/timeEndPeriod()) That still won't really guarantee anything. Even a little load or a single crappy device driver will throw everything off.
是的,您需要了解操作系统的时间量程。在 Windows 上,除非您将时间量更改为 1 毫秒,否则您甚至不会获得 1 毫秒的解析时间。(例如使用 timeBeginPeriod()/timeEndPeriod())这仍然不能真正保证任何事情。即使是一点点负载或一个蹩脚的设备驱动程序也会把所有东西都扔掉。
SetThreadPriority() helps, but is quite dangerous. Bad device drivers can still ruin you.
SetThreadPriority() 有帮助,但非常危险。糟糕的设备驱动程序仍然会毁了你。
You need an ultra-controlled computing environment to make this ugly stuff work at all.
你需要一个超级受控的计算环境才能让这些丑陋的东西发挥作用。
回答by user16523
If you want so much granularity you are in the wrong place (in user space).
如果您想要如此多的粒度,那么您就在错误的位置(在用户空间中)。
Remember that if you are in user space your time is not always precise.
请记住,如果您在用户空间中,您的时间并不总是精确的。
The scheduler can start your thread (or app), and schedule it, so you are depending by the OS scheduler.
调度程序可以启动您的线程(或应用程序)并对其进行调度,因此您取决于操作系统调度程序。
If you are looking for something precise you have to go: 1) In kernel space (like drivers) 2) Choose an RTOS.
如果你正在寻找精确的东西,你必须去:1)在内核空间(如驱动程序)2)选择一个实时操作系统。
Anyway if you are looking for some granularity (but remember the problem with user space ) look to QueryPerformanceCounter Function and QueryPerformanceFrequency function in MSDN.
无论如何,如果您正在寻找一些粒度(但请记住用户空间的问题),请查看 MSDN 中的 QueryPerformanceCounter 函数和 QueryPerformanceFrequency 函数。
回答by Arno
Generally a sleep will last at least until the next system interrupt occurs. However, this
depends on settings of the multimedia timer resources. It may be set to something close to
1 ms, some hardware even allows to run at interrupt periods of 0.9765625 (ActualResolutionprovided by NtQueryTimerResolution
will show 0.9766 but that's actually wrong. They just can't put the correct number into the ActualResolutionformat. It's 0.9765625ms at 1024 interrupts per second).
通常,睡眠至少会持续到下一个系统中断发生。然而,这取决于多媒体定时器资源的设置。它可能设置为接近 1 毫秒,某些硬件甚至允许以 0.9765625 的中断周期运行(由提供的NtQueryTimerResolution
ActualResolution 将显示 0.9766 但这实际上是错误的。他们只是无法将正确的数字放入ActualResolution格式中。它是 0.9765625毫秒,每秒 1024 次中断)。
There is one exception wich allows us to escape from the fact that it may be impossible to sleep for less than the interrupt period: It is the famous Sleep(0)
. This is a very powerful
tool and it is not used as often as it should! It relinquishes the reminder of the thread's time slice. This way the thread will stop until the scheduler forces the thread to get cpu service again. Sleep(0)
is an asynchronous service, the call will force the scheduler to react independent of an interrupt.
有一个例外可以让我们摆脱这样一个事实,即睡眠时间可能少于中断周期:这是著名的Sleep(0)
. 这是一个非常强大的工具,它没有像它应该的那样经常使用!它放弃了线程时间片的提醒。这样线程就会停止,直到调度程序强制线程再次获得 cpu 服务。Sleep(0)
是一个异步服务,调用将强制调度程序独立于中断做出反应。
A second way is the use of a waitable object
. A wait function like WaitForSingleObject()
can wait for an event. In order to have a thread sleeping for any time, also times in the microsecond regime, the thread needs to setup some service thread which will generate an event at the desired delay. The "sleeping" thread will setup this thread and then pause at the wait function until the service thread will set the event signaled.
第二种方法是使用waitable object
. 类似的等待函数WaitForSingleObject()
可以等待一个事件。为了让线程在任何时间(也以微秒范围内的时间)休眠,线程需要设置一些服务线程,该线程将在所需的延迟生成事件。“休眠”线程将设置此线程,然后在等待函数处暂停,直到服务线程将设置事件信号。
This way any thread can "sleep" or wait for any time. The service thread can be of big complexity and it may offer system wide services like timed events at microsecond resolution. However, microsecond resolution may force the service thread to spin on a high resolution time service for at most one interrupt period (~1ms). If care is taken, this can run very well, particulary on multi-processor or multi-core systems. A one ms spin does not hurt considerably on multi-core system, when the affinity mask for the calling thread and the service thread are carefully handled.
这样任何线程都可以“休眠”或等待任何时间。服务线程可能非常复杂,它可以提供系统范围内的服务,例如以微秒分辨率的定时事件。但是,微秒分辨率可能会强制服务线程在最多一个中断周期(~1ms)内旋转高分辨率时间服务。如果小心,它可以运行得很好,特别是在多处理器或多核系统上。当调用线程和服务线程的亲和掩码被仔细处理时,一毫秒的自旋不会对多核系统造成很大的伤害。
Code, description, and testing can be visited at the Windows Timestamp Project
可以在Windows Timestamp Project访问代码、描述和测试
回答by mbyrne215
As several people have pointed out, sleep and other related functions are by default dependent on the "system tick". This is the minimum unit of time between OS tasks; the scheduler, for instance, will not run faster than this. Even with a realtime OS, the system tick is not usually less than 1 ms. While it is tunable, this has implications for the entire system, not just your sleep functionality, because your scheduler will be running more frequently, and potentially increasing the overhead of your OS (amount of time for the scheduler to run, vs. amount of time a task can run).
正如一些人指出的那样,睡眠和其他相关功能默认依赖于“系统滴答”。这是操作系统任务之间的最小时间单位;例如,调度程序的运行速度不会比这更快。即使使用实时操作系统,系统滴答通常也不小于 1 毫秒。虽然它是可调的,但这对整个系统都有影响,而不仅仅是您的睡眠功能,因为您的调度程序将更频繁地运行,并可能增加您的操作系统的开销(调度程序运行的时间量,与任务可以运行的时间)。
The solution to this is to use an external, high-speed clock device. Most Unix systems will allow you to specify to your timers and such a different clock to use, as opposed to the default system clock.
解决方案是使用外部高速时钟设备。大多数 Unix 系统将允许您指定计时器和使用不同的时钟,而不是默认系统时钟。
回答by Rob Walker
What are you waiting for that requires such precision? In general if you needto specify that level of precision (e.g. because of a dependency on some external hardware) you are on the wrong platform and should look at a real time OS.
需要如此精准的你还在等什么?一般而言,如果您需要指定该精度级别(例如,由于对某些外部硬件的依赖),那么您在错误的平台上,应该查看实时操作系统。
Otherwise you should be considering if there is an event you can synchronize on, or in the worse case just busy wait the CPU and use the high performance counter API to measure the elapsed time.
否则你应该考虑是否有一个你可以同步的事件,或者在更糟糕的情况下只是忙着等待 CPU 并使用高性能计数器 API 来测量经过的时间。
回答by Hendrik
Actually using this usleep function will cause a big memory/resource leak. (depending how often called)
实际上使用这个 usleep 函数会导致大的内存/资源泄漏。(取决于调用频率)
use this corrected version (sorry can't edit?)
使用这个更正的版本(抱歉不能编辑?)
bool usleep(unsigned long usec)
{
struct timeval tv;
fd_set dummy;
SOCKET s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
FD_ZERO(&dummy);
FD_SET(s, &dummy);
tv.tv_sec = usec / 1000000ul;
tv.tv_usec = usec % 1000000ul;
bool success = (0 == select(0, 0, 0, &dummy, &tv));
closesocket(s);
return success;
}