如何在Windows上使线程休眠不到一毫秒

时间:2020-03-05 18:59:20  来源:igfitidea点击:

在Windows上,我有一个在Unix上从未遇到过的问题。这就是使线程休眠不到一毫秒的方法。在Unix上,通常有多种选择(睡眠,usleep和nanosleep)可以满足需求。但是,在Windows上,只有"睡眠"级别为毫秒。

在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上实现相同的目标?

解决方案

回答

在Windows上,使用select会强制我们包括Winsock库,该库必须在应用程序中像这样初始化:

WORD wVersionRequested = MAKEWORD(1,0);
WSADATA wsaData;
WSAStartup(wVersionRequested, &wsaData);

然后,select将不允许我们在没有任何套接字的情况下被调用,因此我们必须做更多的事情才能创建微睡眠方法:

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);
}

所有这些创建的usleep方法在成功时都返回零,而对于错误则返回非零。

回答

这表明对睡眠功能有误解。我们传递的参数是最短睡眠时间。不能保证线程会在指定的时间之后立即唤醒。实际上,线程根本不会"唤醒",而是由调度程序选择执行。调度程序可能选择等待比请求的睡眠时间更长的时间来激活一个线程,特别是如果此时另一个线程仍处于活动状态。

回答

正如Joel所说,我们不能在这么短的时间内有意义地"睡眠"(即放弃我们计划的CPU)。如果我们想延迟一小段时间,则需要旋转,反复检查一个合适的高分辨率计时器(例如``性能计时器''),并希望高优先级的东西仍然不会抢先。

如果我们真正关心如此短时间的准确延迟,则不应使用Windows。

回答

使用winmm.lib中提供的高分辨率计时器。参见示例。

回答

我们还在等什么需要这种精度的东西?通常,如果我们需要指定该精度级别(例如,由于对某些外部硬件的依赖性),则我们使用的平台不正确,应该查看实时操作系统。

否则,我们应该考虑是否存在可以同步的事件,或者在更坏的情况下,只是忙于等待CPU并使用高性能计数器API来测量经过的时间。

回答

是的,我们需要了解操作系统的时间范围。在Windows上,除非将时间段更改为1ms,否则我们甚至都不会获得1ms的分辨率时间。 (例如使用timeBeginPeriod()/ timeEndPeriod())实际上仍然不能保证任何东西。即使是很小的负载或者单个糟糕的设备驱动程序,都将使所有东西丢掉。

SetThreadPriority()有帮助,但是非常危险。不良的设备驱动程序仍然会毁了我们。

我们需要一个超受控的计算环境才能使这些难看的东西完全起作用。

回答

尝试boost :: xtime和timed_wait()

具有纳秒级精度。

回答

正如一些人指出的那样,睡眠和其他相关功能默认情况下取决于"系统刻度"。这是OS任务之间的最小时间单位;例如,调度程序的运行速度不会超过此速度。即使使用实时操作系统,系统时钟通常也不会少于1毫秒。尽管它是可调的,但这不仅影响睡眠功能,还影响整个系统,因为调度程序将更频繁地运行,并可能增加操作系统的开销(调度程序运行的时间与任务可以运行的时间)。

解决方案是使用外部高速时钟设备。大多数Unix系统将允许我们指定计时器以及要使用的其他时钟,而不是默认的系统时钟。

回答

如果我们想要太多的粒度,那么我们来错地方了(在用户空间中)。

请记住,如果我们在用户空间中,则时间并不总是精确的。

调度程序可以启动线程(或者应用程序)并对其进行调度,因此我们取决于操作系统调度程序。

如果我们正在寻找精确的东西,则必须去:
1)在内核空间(如驱动程序)
2)选择一个实时操作系统。

无论如何,如果我们正在寻找某种粒度(但请记住用户空间的问题),
MSDN中的QueryPerformanceCounter函数和QueryPerformanceFrequency函数。

回答

只需使用Sleep(0)。 0显然小于毫秒。现在,这听起来很有趣,但我是认真的。 Sleep(0)告诉Windows我们现在没有任何事情要做,但是我们确实希望在调度程序再次运行时被重新考虑。而且由于显然无法在调度程序本身运行之前调度线程运行,因此这是最短的延迟。

请注意,我们可以将微秒数传递给usleep,但是无效usleep(__ int64 t){Sleep(t / 1000); }不能保证实际睡那个时期。

回答

我有同样的问题,似乎没有什么比ms更快的了,即使Sleep(0)也是如此。我的问题是客户端和服务器应用程序之间的通信,在此我使用_InterlockedExchange函数测试并设置一个位,然后我进入Sleep(0)。

我确实需要以这种方式每秒执行数千次操作,但它的速度不如我计划的快。

由于我有一个与用户打交道的瘦客户端,后者又调用了一个代理,该代理然后与线程进行通信,因此我将尽快将线程与代理合并,因此不需要事件接口。

只是为了让大家知道这个睡眠有多慢,我进行了一个10秒钟的测试,执行一个空循环(得到18,000,000个循环),而有了该事件,我只有180,000个循环。也就是说,慢100倍!

回答

实际上,使用此usleep函数将导致大量内存/资源泄漏。 (取决于调用频率)

使用此更正的版本(对不起,无法编辑?)

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;
}