WaitHandle.WaitOne(0)是用来测试等待句柄状态的,它并不阻塞,所以用它来进行误差修正类似于自旋,但不如直接使用自旋可靠。
(3)Socket.Poll
Socket.Poll方法的参数是以微秒为单位,理论上,它是使用了网卡的硬件来定时,精度很高。然而,由于阻塞的实现仍然要依赖线程,所以它也只能达到 1ms 的精度。
它的优势是比Thread.Sleep和WaitHandle.WaitOne要更稳定,误差也更小,可以不需要修正,但要占用一个 Socket 端口。
没有任务负载的情况下(纯粹循环调用Poll(1)),阻塞时长稳定在 1ms;而有任务负载时,则和WaitOne类似,可能仅阻塞近乎于 0 的时间。所以它阻塞的时长范围是 0 到 1ms 多。
Socket.Poll(0)是用来测试 Socket 状态的,但它会阻塞,而且可能阻塞高达 6ms,所以不能用它来进行误差修正。
2.3timeSetEvent
timeSetEvent和之前提到的timeBeginPeriod一样属于 winmm.dll 提供的多媒体定时器功能。它可以直接当作定时器使用,也是提供 1ms 的精度。在不需要的时候使用timeKillEvent来关闭。
它的稳定性和精度也很高,如果需要 1ms 的定时,而又不能使用自旋,那么这是最理想的方案。
虽然 MSDN 上说timeSetEvent是个过时的方法,应该用CreateTimerQueueTimer替换。但是CreateTimerQueueTimer精度和稳定性都不如多媒体定时器,所以在需要高精度的时候,只能使用timeSetEvent。
3、定时器实现
需要注意的是,无论自旋还是阻塞,显然定时器都应该运行在独立的线程,不能干扰使用方线程工作。而对于高精度定时器来说,触发事件以执行任务的线程一般都在定时器线程内,而不是再使用独立的任务线程。
这是因为高精度定时场景下,执行任务的时间开销很可能大于定时器的时间间隔,如果默认就在其它线程执行任务,可能导致占用大量线程。所以应该把控制权交给用户,让用户在需要的时候自行调度任务执行的线程。
3.1触发模式
由于在定时器线程执行任务,所以定时器的触发就产生了三种模式。以下是它们的说明和主循环伪代码:
(1)固定时间框架










