c# 高精度计时器
使用 timeSetEvent
MMRESULT timeSetEvent( UINT uDelay,
UINT uResolution,
LPTIMECALLBACK lpTimeProc,
WORD dwUser,
UINT fuEvent )
uDelay:以毫秒指定事件的周期。
Uresolution:以毫秒指定延时的精度,数值越小定时器事件分辨率越高。缺省值为1ms。
LpTimeProc:指向一个回调函数。
DwUser:存放用户提供的回调数据。
FuEvent:指定定时器事件类型:
TIME_ONESHOT:uDelay毫秒后只产生一次事件
TIME_PERIODIC :每隔uDelay毫秒周期性地产生事件。
c#包装类:
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
/// <summary>
///
/// </summary>
public class MultimediaTimerWapper : IDisposable
{
private bool disposed = false;
private int interval;
private int resolution;
/// <summary>
/// 当前定时器实例ID
/// </summary>
private uint timerId;
// 保持定时器回调以防止垃圾收集。
private readonly MultimediaTimerCallback Callback;
/// <summary>
/// API使用的回调
/// </summary>
public event EventHandler Timer;
public MultimediaTimerWapper()
{
Callback = new MultimediaTimerCallback(TimerCallbackMethod);
Resolution = 0;
}
~MultimediaTimerWapper()
{
Dispose(false);
}
/// <summary>
/// 注意最小分辨率为 0,表示可能的最高分辨率。
/// </summary>
public int Resolution
{
get
{
return resolution;
}
set
{
CheckDisposed();
if (value < 0)
throw new ArgumentOutOfRangeException("value");
resolution = value;
}
}
/// <summary>
/// 是否在运行
/// </summary>
public bool IsRunning
{
get { return timerId != 0; }
}
/// <summary>
/// 启动一个定时器实例
/// </summary>
/// <param name="ms">以毫秒为单位的计时器间隔</param>
/// <param name="repeat">如果为 true 设置重复事件,否则设置一次性</param>
public void Start(uint ms,bool repeat)
{
//杀死任何现有的计时器
CheckDisposed();
if (IsRunning)
throw new InvalidOperationException("Timer is already running");
//Set the timer type flags
fuEvent f = fuEvent.TIME_CALLBACK_FUNCTION | (repeat ? fuEvent.TIME_PERIODIC : fuEvent.TIME_ONESHOT);
// Event type = 0, one off event
// Event type = 1, periodic event
uint userCtx = 0;
lock (this)
{
timerId = NativeMethods.TimeSetEvent(ms, (uint)Resolution, Callback, ref userCtx, (uint)f);
if (timerId == 0)
{
int error = Marshal.GetLastWin32Error();
throw new Win32Exception(error);
}
}
}
/// <summary>
///
/// </summary>
public void Stop()
{
CheckDisposed();
if (!IsRunning)
throw new InvalidOperationException("Timer has not been started");
StopInternal();
}
private void StopInternal()
{
lock (this)
{
if(timerId != 0)
{
NativeMethods.TimeKillEvent(timerId);
timerId = 0;
}
}
}
public void Dispose()
{
Dispose(true);
}
private void TimerCallbackMethod(uint id, uint msg, ref uint userCtx, uint rsv1, uint rsv2)
{
Timer?.Invoke(this, EventArgs.Empty);
}
private void CheckDisposed()
{
if (disposed)
throw new ObjectDisposedException("MultimediaTimer");
}
private void Dispose(bool disposing)
{
if (disposed)
return;
disposed = true;
if (IsRunning)
{
StopInternal();
}
if (disposing)
{
Timer = null;
GC.SuppressFinalize(this);
}
}
}
internal delegate void MultimediaTimerCallback(uint id, uint msg, ref uint userCtx, uint rsv1, uint rsv2);
/// <summary>
/// 此结构包含有关计时器分辨率的信息。单位是ms
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct TIMECAPS
{
/// <summary>
/// 支持的最小期限。
/// </summary>
public uint wPeriodMin;
/// <summary>
/// 支持的最大期限。
/// </summary>
public uint wPeriodMax;
}
/// <summary>
/// 定时器类型定义
/// </summary>
[Flags]
public enum fuEvent : uint
{
TIME_ONESHOT = 0, //Event occurs once, after uDelay milliseconds.
TIME_PERIODIC = 1,
TIME_CALLBACK_FUNCTION = 0x0000, /* callback is function */
//TIME_CALLBACK_EVENT_SET = 0x0010, /* callback is event - use SetEvent */
//TIME_CALLBACK_EVENT_PULSE = 0x0020 /* callback is event - use PulseEvent */
}
internal static class NativeMethods
{
/// <summary>
/// 此函数启动指定的计时器事件。
/// </summary>
/// <param name="msDelay">事件延迟,以毫秒为单位。如果该值不在计时器支持的最小和最大事件延迟范围内,则该函数返回错误。</param>
/// <param name="msResolution">计时器事件的分辨率,以毫秒为单位。分辨率越高,分辨率越高;零分辨率表示周期性事件应该以最大可能的精度发生。但是,为减少系统开销,应使用适合您的应用程序的最大值。</param>
/// <param name="callback">如果fuEvent指定TIME_CALLBACK_EVENT_SET或TIME_CALLBACK_EVENT_PULSE标志,则fptc参数将解释为事件对象的句柄。事件将在单个事件完成时设置或发出脉冲,或者在周期性事件完成时定期设置或触发。对于fuEvent的任何其他值,fptc参数将被解释为函数指针。</param>
/// <param name="userCtx">用户提供的回调数据。</param>
/// <param name="fuEvent">计时器事件类型。下表显示了fuEvent参数可以包含的值。</param>
[DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeSetEvent")]
internal static extern uint TimeSetEvent(uint msDelay, uint msResolution, MultimediaTimerCallback callback, ref uint userCtx, uint fuEvent);
/// <summary>
/// 此功能取消指定的计时器事件。
/// </summary>
/// <param name="uTimerId">要取消的计时器事件的标识符。此标识符由timeSetEvent函数返回,该函数启动指定的计时器事件。</param>
/// <returns></returns>
[DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeKillEvent")]
internal static extern void TimeKillEvent(uint uTimerId);
/// <summary>
/// 此函数查询计时器设备以确定其分辨率。
/// </summary>
/// <param name="ptc">指向TIMECAPS结构的指针。该结构充满了有关计时器设备分辨率的信息。</param>
/// <param name="cbtc">TIMECAPS结构的大小(以字节为单位)。</param>
/// <returns>如果成功,则返回TIMERR_NOERROR,如果未能返回计时器设备功能,则返回TIMERR_STRUCT。</returns>
[DllImport("winmm.dll")]
internal static extern uint timeGetDevCaps(ref TIMECAPS ptc, int cbtc);
[DllImport("Winmm.dll", CharSet = CharSet.Auto)]
internal static extern uint timeGetTime();
[DllImport("Winmm.dll", CharSet = CharSet.Auto)]
internal static extern uint timeBeginPeriod(uint uPeriod);
[DllImport("Winmm.dll", CharSet = CharSet.Auto)]
internal static extern uint timeEndPeriod(uint uPeriod);
}
调用方式:
static void Main(string[] args)
{
MultimediaTimerWapper multimediaTimer = new MultimediaTimerWapper();
multimediaTimer.Resolution = 1;
multimediaTimer.Timer += MultimediaTimer_Timer;
multimediaTimer.Start(1000,false);
Console.ReadKey();
multimediaTimer.Stop();
}
private static void MultimediaTimer_Timer(object sender, EventArgs e)
{
Console.WriteLine("执行完成");
}
使用 CreateTimerQueueTimer
定时器
定时器队列(Timer Queue)可以使用CreateTimerQueue
函数创建。定时器队列中的定时器是轻量级对象,可以在一定时间间隔之后调用指定的回调函数(可以只调用一次,也可以是周期性的)。这种等待操作由线程池中某个线程处理的(系统级别)。
向定时器队列中添加定时器可以使用CreateTimerQueueTimer
函数。更新一个计时器队列中的计时器可以使用 ChangeTimerQueueTimer
函数。这两个函数中你可以指定一个回调函数,如果设定时间到达,线程池中某个线程会调用该回调函数。
使用 DeleteTimerQueueTimer
函数可以取消挂起的计时器。当不再使计时器队列的时候,调用 DeleteTimerQueueEx
函数删除计时器队列,该函数调用是会取消并删除任何在该队列中等待的计时器。
1.HANDLE CreateTimerQueue()
:该函数创建一个定时器队列,定时器队列对一组定时器进行组织安排。一旦拥有一个定时器队列,就可以在该队列中创建下面的定时器:
2.BOOL CreateTimerQueueTimer(PHANDLE phNewTimer, HANDLE hTimerQueue,WAITORTIMERCALLBACK pfnCallback,PVOID pvContext,DWORD dwDueTimer,DWORD dwPeriod,ULONG dwFlags)
:如果只是创建少数几个定时器,只需要为hTimerQueue
参数传递NULL
,并且完全避免调用CreateTimerQueue
函数。这会使用默认的定时器队列,并且简化你的代码。工作回调函数必须采用下面的原型: VOID WINAPI WaitOrTimerCallback(PVOID pvContext,BOOL fTimerOrWatiFired)
;
dwFlags
参数的取值:WT_EXECUTEDEFAULT
(用非I/O组件处理工作项目),WT_EXECUTEINIOTHREAD
(用I/O组件),WT_EXECUTEPERSISTENTTHREAD
(用定时器),WT_EXECUTELONGFUNCTION
(工作项目需要花费较长时间时)
还有一个标志是WT_EXECUTEINTIMERTHREAD
,它使定时器组件的线程可以执行工作项目,虽然这可以使工作项目的运行效率更高,但是这非常危险,如果工作项目长时间中断运行,那么等待定时器的线程就无法执行任何其他操作。如果打算这么做,那么该代码应该迅速执行,不应该中断。
回调函数的原型:VOID WINAPI WaitOrTimerCallbackFunc( PVOID pvContext, BOOLEAN fTimerOrWaitFired)
;
3.可以调用 BOOL ChangeTimerQueueTimer(HANDLE hTimerQueue ,HANDLE hTimer, ULONG dwDueTimer,ULONG dwPeriod)
函数来改变它的到期时间和到期周期。
4.当不再使用定时器时,必须通过调用下面的函数将它删除: BOOL DeleteTimerQueueTimer(HANDLE hTimerQueue,HANDLE hTimer, HANDLE hCompletionEvent)
;
5. 当要删除定时器队列:BOOL DeleteTimerQueueEX(HANDLE hTimerQueue,HANDLE hCompletionEvent)
;
c#包装类:
using System;
using System.Runtime.InteropServices;
/// <summary>
/// 高精度计时器
/// </summary>
public class TimerQueueTimerWapper : IDisposable
{
/// <summary>
/// Handle to the timer.(处理定时器。)
/// </summary>
private IntPtr phNewTimer;
#region Win32 TimerQueueTimer 函数
/// <summary>
///
/// </summary>
/// <param name="phNewTimer">Pointer to a handle; this is an out value(指向句柄的指针; 这是一个out值)</param>
/// <param name="TimerQueue">Timer queue handle. For the default timer queue, NULL (定时器队列句柄。 对于默认计时器队列,NULL)</param>
/// <param name="Callback">Pointer to the callback function (指向回调函数的指针)</param>
/// <param name="Parameter">Value passed to the callback function (传递给回调函数的值)</param>
/// <param name="DueTime">Time (milliseconds), before the timer is set to the signaled state for the first time
/// (时间(毫秒),在定时器第一次设置为信号状态之前)</param>
/// <param name="Period">Time period (milliseconds). If zero, timer is signaled only once(计时器周期(毫秒)。如果为零,则计时器仅发出一次信号)</param>
/// <param name="Flags">One or more of the next values (table taken from MSDN):
/// WT_EXECUTEINTIMERTHREAD The callback function is invoked by the timer thread itself. This flag should be used only for short tasks or it could affect other timer operations.
/// WT_EXECUTEINIOTHREAD The callback function is queued to an I/O worker thread. This flag should be used if the function should be executed in a thread that waits in an alertable state.
/// </param>
/// <remarks>
/// The callback function is queued as an APC. Be sure to address reentrancy issues if the function performs an alertable wait operation.
/// WT_EXECUTEINPERSISTENTTHREAD The callback function is queued to a thread that never terminates. This flag should be used only for short tasks or it could affect other timer operations.
/// Note that currently no worker thread is persistent, although no worker thread will terminate if there are any pending I/O requests.
/// WT_EXECUTELONGFUNCTION Specifies that the callback function can perform a long wait. This flag helps the system to decide if it should create a new thread.
/// WT_EXECUTEONLYONCE The timer will be set to the signaled state only once.
/// </remarks>
/// <returns></returns>
[DllImport("kernel32.dll")]
private static extern bool CreateTimerQueueTimer(out IntPtr phNewTimer, IntPtr TimerQueue,WaitOrTimerDelegate Callback,
IntPtr Parameter,uint DueTime,uint Period, uint Flags);
/// <summary>
///
/// </summary>
/// <param name="timerQueue">A handle to the (default) timer queue</param>
/// <param name="timer">A handle to the timer</param>
/// <param name="completionEvent">A handle to an optional event to be signaled when the function is successful and all callback functions have completed. Can be NULL.</param>
/// <returns></returns>
[DllImport("kernel32.dll")]
private static extern bool DeleteTimerQueueTimer(IntPtr timerQueue,IntPtr timer, IntPtr completionEvent);
[DllImport("kernel32.dll")]
private static extern bool DeleteTimerQueue(IntPtr TimerQueue);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);
#endregion
public delegate void WaitOrTimerDelegate(IntPtr lpParameter, bool timerOrWaitFired);
public TimerQueueTimerWapper()
{
}
/// <summary>
/// 创建定时器
/// </summary>
/// <param name="dueTime">定时器第一次设置为信号状态之前的时间(毫秒)。</param>
/// <param name="period">Period - 计时器周期(毫秒)。 如果为零,则计时器仅发出一次信号。</param>
/// <param name="callbackDelegate">回调函数参数</param>
public void Create(uint dueTime, uint period, WaitOrTimerDelegate callbackDelegate)
{
IntPtr pParameter = IntPtr.Zero;
bool success = CreateTimerQueueTimer(
// Timer handle
out phNewTimer,
// Default timer queue. IntPtr.Zero is just a constant value that represents a null pointer.
IntPtr.Zero,
// Timer callback function
callbackDelegate,
// Callback function parameter
pParameter,
// Time (milliseconds), before the timer is set to the signaled state for the first time.
// 定时器第一次设置为信号状态之前的时间(毫秒)。
dueTime,
// Period - Timer period (milliseconds). If zero, timer is signaled only once.
//Period - 计时器周期(毫秒)。 如果为零,则计时器仅发出一次信号。
period,
(uint)Flag.WT_EXECUTEINIOTHREAD);
if (!success)
throw new QueueTimerException("Error creating QueueTimer");
}
/// <summary>
/// 删除定时器
/// </summary>
public void Delete()
{
//bool success = DeleteTimerQueue(IntPtr.Zero);
bool success = DeleteTimerQueueTimer(
IntPtr.Zero, // TimerQueue - A handle to the (default) timer queue
phNewTimer, // Timer - A handle to the timer
IntPtr.Zero // CompletionEvent - A handle to an optional event to be signaled when the function is successful and all callback functions have completed. Can be NULL.
);
int error = Marshal.GetLastWin32Error();
//CloseHandle(phNewTimer);
}
private enum Flag
{
WT_EXECUTEDEFAULT = 0x00000000,
WT_EXECUTEINIOTHREAD = 0x00000001,
//WT_EXECUTEINWAITTHREAD = 0x00000004,
WT_EXECUTEONLYONCE = 0x00000008,
WT_EXECUTELONGFUNCTION = 0x00000010,
WT_EXECUTEINTIMERTHREAD = 0x00000020,
WT_EXECUTEINPERSISTENTTHREAD = 0x00000080,
//WT_TRANSFER_IMPERSONATION = 0x00000100
}
#region IDisposable Members
void IDisposable.Dispose()
{
Delete();
}
#endregion
}
public class QueueTimerException : Exception
{
public QueueTimerException(string message) : base(message)
{
}
public QueueTimerException(string message, Exception innerException)
: base(message, innerException)
{
}
}
调用方式:
static void Main(string[] args)
{
TimerQueueTimerWapper qt = new TimerQueueTimerWapper();
TimerQueueTimerWapper.WaitOrTimerDelegate callbackDelete = new TimerQueueTimerWapper.WaitOrTimerDelegate(QueueTimerCallback);
qt.Create(2000, 0, callbackDelete);
Console.ReadKey();
qt.Delete();
}
private static void QueueTimerCallback(IntPtr lpParameter, bool timerOrWaitFired)
{
Console.WriteLine("执行完成");
}