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("執行完成");
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM