我希望创建一个高频回调线程。本质上,我需要一个功能来执行一个正常的高频(高达100赫兹)间隔。我意识到windows有一个正常的线程执行片,大约是15 is。我想指定一个可以比15 be更快的规则间隔。
这就是我想要达到的目标。我有一个外部设备,需要在一定的时间间隔内发送消息。间隔是可变的,视情况而定。我希望我不会需要超过100赫兹(10毫秒)的消息率。
当然,我可以实现一个spin循环,但是,我希望有一个解决方案,不会需要那么多浪费资源。
提供的问题/答案链接不能解决此问题。虽然我同意这个问题被问了几个不同的方法,但并没有一个好的解决办法来真正解决这个问题。
大多数答案提供了关于使用秒表和手动执行计时任务的谈话,这完全是CPU密集型的。唯一可行的解决方案是使用多媒体计时器,正如Haans提到的那样,这种计时器有几个陷阱。不过,我已经找到了另一个解决方案,我将在下面添加。我不知道目前的陷阱,但我计划做一些测试和研究。我仍然对有关解决办法的评论感兴趣。
WINAPI调用
BOOL WINAPI CreateTimerQueueTimer(
_Out_ PHANDLE phNewTimer,
_In_opt_ HANDLE TimerQueue,
_In_ WAITORTIMERCALLBACK Callback,
_In_opt_ PVOID Parameter,
_In_ DWORD DueTime,
_In_ DWORD Period,
_In_ ULONG Flags
);和
BOOL WINAPI DeleteTimerQueueTimer(
_In_opt_ HANDLE TimerQueue,
_In_ HANDLE Timer,
_In_opt_ HANDLE CompletionEvent
);链接- http://msdn.microsoft.com/en-us/library/windows/desktop/ms682485%28v=vs.85%29.aspx我正在使用PInvoke来完成这个任务。然而,在处理多媒体计时器时也需要这样做。
我的PInvoke签名给那些感兴趣的人。Pinvoke链接
[DllImport("kernel32.dll")]
static extern bool CreateTimerQueueTimer(out IntPtr phNewTimer,
IntPtr TimerQueue, WaitOrTimerDelegate Callback, IntPtr Parameter,
uint DueTime, uint Period, uint Flags);
// This is the callback delegate to use.
public delegate void WaitOrTimerDelegate (IntPtr lpParameter, bool TimerOrWaitFired);
[DllImport("kernel32.dll")]
static extern bool DeleteTimerQueueTimer(IntPtr TimerQueue, IntPtr Timer,
IntPtr CompletionEvent);使用CreateTimerQueueTimer启动计时器回调。使用DeleteTimerQueueTimer停止计时器回调。这有点灵活,因为您也可以创建自定义队列。但是,如果只需要一个实例,最简单的实现就是使用默认队列。
我使用带旋转环的秒表测试了这个解决方案,在计时方面我收到的结果几乎相同。然而,CPU负载在我的机器上有很大的不同。
具有旋转环的秒表- ~12-15%的恒定CPU负载(约为我的核心的50% ) CreateTimerQueueTimer - ~3-4%的恒定CPU负载。
我还觉得使用CreateTimerQueueTimer选项可以减少代码维护。因为它不需要将逻辑添加到代码流中。
发布于 2013-05-19 08:48:11
有许多不准确的信息传播通过链接和评论。是的,在大多数机器上,默认情况下时钟滴答中断为1/64秒= 15.625毫秒,但它可以更改。还有一些机器似乎以另一种速度运转的原因。winmm.dll提供的Windows多媒体api允许您对其进行修补。
你不想做的就是用秒表。只有在不断检查间隔是否过去的热循环中使用它时,才能得到准确的间隔度量。当一个线程的量程到期时,Windows将不友好地对待它,当其他线程争夺处理器时,线程将不会在一段时间内重新调度运行。这种效果很容易被忽略,因为您通常不使用其他进程来调试代码,而是积极地运行和燃烧cpu时间。
您想要使用的函数是timeSetEvent(),它提供了一个非常精确的计时器,可以低到1毫秒。它是自校正的,如果有必要(并且可能的话),当上一次回调由于调度限制而延迟时,可以缩短时间间隔。但是,请注意,回调是从线程池线程(类似于System.Threading.Timer )发出的,因此要确保使用安全的联锁,并采取对策,以确保不会因为重新进入而带来麻烦。
另一种完全不同的方法是timeBeginPeriod(),它改变时钟中断率。这有许多副作用,因为一个Thread.Sleep()变得更准确了。这往往是更容易的解决方案,因为您可以使这个同步。只要睡上1毫秒,你就能得到中断率。一些要使用的代码演示了它的工作方式:
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Threading;
class Program {
static void Main(string[] args) {
timeBeginPeriod(10);
while (!Console.KeyAvailable) {
var sw = Stopwatch.StartNew();
for (int ix = 0; ix < 100; ++ix) Thread.Sleep(1);
sw.Stop();
Console.WriteLine("{0} msec", sw.ElapsedMilliseconds);
}
timeEndPeriod(10);
}
[DllImport("winmm.dll")]
public static extern uint timeBeginPeriod(int msec);
[DllImport("winmm.dll")]
public static extern uint timeEndPeriod(int msec);
}我的机器上的输出:
1001 msec
995 msec
999 msec
999 msec
999 msec
991 msec
999 msec
999 msec
999 msec
999 msec
999 msec
990 msec
999 msec
998 msec
...但是,请注意这种方法的问题,如果您的计算机上的另一个进程已经将时钟中断率降低到10 m秒以下,那么您将无法从这段代码中得到一秒钟。这是非常尴尬的处理,你只能让它真正的安全,要求1毫秒的速度和睡眠10。不要运行在电池操作的机器。请支持timeSetEvent()。
发布于 2013-05-19 00:26:42
您可能需要尝试StopWatch,它使用与DirectX相同的高性能计时器。
发布于 2013-05-19 06:53:37
请注意,15 is是有效最小Sleep时间的部分原因是,它可以用这个数量级来实际交换您的进程,交换另一个进程,让它有时间做任何事情,交换掉它,然后再交换您的进程。假设您有多个核心,那么使用一个来手动更新100 as可能是最好的,特别是当您想跳过几个手动生成的垃圾收集周期和/或按照建议使用C/C++的时候。
https://stackoverflow.com/questions/16630238
复制相似问题