c# 微秒级定时器,高精度定时器
整个代码,封装成类
using System;
using System.Runtime.InteropServices;
namespace winTest
{
/// <summary>
/// 基于系统性能计数器的定时器,计数单位是1微秒=1/1000毫秒
/// 注意:该定时器会独占一个CPU核心,尝试定时器与主程序运行在同一核心将导致程序失去响应
/// </summary>
public class MTimer
{
/// <summary>
/// 获取当前系统性能计数
/// </summary>
/// <param name="lpPerformanceCount"></param>
/// <returns></returns>
[DllImport("Kernel32.dll")]
private static extern bool QueryPerformanceCounter(out long lpPerformanceCount);
/// <summary>
/// 获取当前系统性能频率
/// </summary>
/// <param name="lpFrequency"></param>
/// <returns></returns>
[DllImport("Kernel32.dll")]
private static extern bool QueryPerformanceFrequency(out long lpFrequency);
/// <summary>
/// 指定某一特定线程运行在指定的CPU核心
/// </summary>
/// <param name="hThread"></param>
/// <param name="dwThreadAffinityMask"></param>
/// <returns></returns>
[DllImport("kernel32.dll")]
static extern UIntPtr SetThreadAffinityMask(IntPtr hThread, UIntPtr dwThreadAffinityMask);
/// <summary>
/// 获取当前线程的Handler
/// </summary>
/// <returns></returns>
[DllImport("kernel32.dll")]
static extern IntPtr GetCurrentThread();
/// <summary>
/// 是否销毁定时器
/// </summary>
private bool _Dispose = false;
/// <summary>
/// 是否正在运行定时器
/// </summary>
private bool _BRunTimer = false;
/// <summary>
/// 首次启动延时(微秒)
/// </summary>
private uint _Delay = 0;
/// <summary>
/// 定时器周期(微秒)
/// </summary>
private long _Period = 10;
/// <summary>
/// 定时器运行时独占的CPU核心索引序号
/// </summary>
private byte _CpuIndex = 0;
/// <summary>
/// 系统性能计数频率(每秒)
/// </summary>
private long _Freq = 0;
/// <summary>
/// 系统性能计数频率(每微秒)
/// </summary>
private long _Freqmms = 0;
/// <summary>
/// 回调函数定义
/// </summary>
private OnTickHandle Tick;
/// <summary>
/// 根据CPU的索引序号获取CPU的标识序号
/// </summary>
/// <param name="idx"></param>
/// <returns></returns>
private ulong GetCpuID(int idx)
{
ulong cpuid = 0;
if (idx < 0 || idx >= System.Environment.ProcessorCount)
{
idx = 0;
}
cpuid |= 1UL << idx;
return cpuid;
}
/// <summary>
/// 定时器构造函数
/// </summary>
/// <param name="delay">首次启动定时器延时时间(微秒)</param>
/// <param name="period">定时器触发的周期(微秒)</param>
/// <param name="cpuIndex">指定定时器线程独占的CPU核心索引,必须>0,不允许为定时器分配0#CPU</param>
/// <param name="tick">定时器触发时的回调函数</param>
public MTimer(uint delay, uint period, byte cpuIndex, OnTickHandle tick)
{
Tick = tick;
_Delay = delay;
_Period = period;
_CpuIndex = cpuIndex;
long freq = 0;
QueryPerformanceFrequency(out freq);
if (freq > 0)
{
_Freq = freq;
_Freqmms = freq / 1000000;//每微秒性能计数器跳跃次数
}
else
{
throw new Exception("初始化定时器失败");
}
if (_CpuIndex == 0)
{
throw new Exception("定时器不允许被分配到0#CPU");
}
if (_CpuIndex >= System.Environment.ProcessorCount)
{
throw new Exception("为定时器分配了超出索引的CPU");
}
}
private System.Threading.Thread _threadRumTimer;
/// <summary>
/// 开启定时器
/// </summary>
public void Open()
{
if (Tick != null)
{
_threadRumTimer = new System.Threading.Thread(new System.Threading.ThreadStart(RunTimer));
_threadRumTimer.Start();
}
}
/// <summary>
/// 运行定时器
/// </summary>
private void RunTimer()
{
UIntPtr up = UIntPtr.Zero;
if (_CpuIndex != 0)
up = SetThreadAffinityMask(GetCurrentThread(), new UIntPtr(GetCpuID(_CpuIndex)));
if (up == UIntPtr.Zero)
{
throw new Exception("为定时器分配CPU核心时失败");
}
long q1, q2;
QueryPerformanceCounter(out q1);
QueryPerformanceCounter(out q2);
if (_Delay > 0)
{
while (q2 < q1 + _Delay * _Freqmms)
{
QueryPerformanceCounter(out q2);
}
}
QueryPerformanceCounter(out q1);
QueryPerformanceCounter(out q2);
while (!_Dispose)
{
_BRunTimer = true;
QueryPerformanceCounter(out q2);
if (q2 > q1 + _Freqmms * _Period)
{
//***********回调***********//
if (!_Dispose)
Tick(this, (q2 - q1) / (_Freqmms * _Period), (q2 - q1) / _Freqmms);
q1 = q2;
//System.Windows.Forms.Application.DoEvents();//会导致线程等待windows消息循环,时间损失15ms以上
}
_BRunTimer = false;
}
}
/// <summary>
/// 销毁当前定时器所占用的资源
/// </summary>
public void Dispose()
{
_Dispose = true;
while (_BRunTimer)
System.Windows.Forms.Application.DoEvents();//在工作未完成之前,允许处理消息队列,防止调用者挂起
if (_threadRumTimer != null)
_threadRumTimer.Abort();
}
/// <summary>
/// 定时器事件的委托定义
/// </summary>
/// <param name="sender">事件的发起者,即定时器对象</param>
/// <param name="JumpPeriod">上次调用和本次调用跳跃的周期数</param>
/// <param name="interval">上次调用和本次调用之间的间隔时间(微秒)</param>
public delegate void OnTickHandle(object sender, long JumpPeriod, long interval);
}
}
下面是测试代码
MTimer m;
private void fun(object sender, long JumpPeriod, long interval)
{
Console.WriteLine(DateTime.Now.Millisecond);
}
private void OnOpen(object sender, EventArgs e)
{
m = new MTimer(100, 500, 1, fun);
m.Open();
}
private void OnClose(object sender, EventArgs e)
{
m.Dispose();
}
实现的效果是一秒输出当前毫秒数2000次。
写在最后,些方法要求电脑cpu核心数量大于1