

























System.Threading.Timer 是 .NET 中一个轻量级的、基于线程池的定时器。它不是创建一个长期运行的线程,而是利用操作系统的定时器机制,在指定时间后将回调函数投递到线程池执行。
// Timer 的完整构造函数
public Timer(
TimerCallback callback, // 回调委托
object? state, // 状态参数
int dueTime, // 首次执行前等待时间(毫秒)
int period // 后续执行间隔时间(毫秒)
)
四种常用构造方式:
// 1. 单次执行,永不重复
new Timer(callback, null, 5000, Timeout.Infinite);
// 2. 立即开始,每1秒执行一次
new Timer(callback, null, 0, 1000);
// 3. 5秒后开始,每2秒执行一次
new Timer(callback, null, 5000, 2000);
// 4. 永不执行(可以后续Change)
new Timer(callback, null, Timeout.Infinite, Timeout.Infinite);
public class TimerDemo
{
private Timer _timer;
public void StartTimer()
{
// 创建定时器(不会阻塞)
_timer = new Timer(DoWork, null, 5000, 1000);
// 此时定时器已在运行,5秒后第一次触发
}
private void DoWork(object state)
{
// 这个代码在线程池线程中执行
Console.WriteLine($"执行时间:{DateTime.Now}");
}
public void StopTimer()
{
// 释放定时器资源
_timer?.Dispose();
}
public void ChangeTimer()
{
// 修改定时器:10秒后执行,然后每2秒一次
_timer.Change(10000, 2000);
// 停止定时器
_timer.Change(Timeout.Infinite, Timeout.Infinite);
}
}
graph TB subgraph ".NET层面" A[创建Timer对象] --> B[注册到Timer队列] B --> C[等待触发] end subgraph "操作系统层面" D[系统时钟中断] --> E[检查定时器队列] E --> F[到期?] F -->|是| G[触发完成端口] end subgraph "线程池层面" G --> H[IO完成线程] H --> I[执行回调函数] end C --> D
在 Windows 上,.NET Timer 底层使用了 Waitable Timer Objects:
// Windows 底层API(伪代码)
class WindowsTimerImplementation
{
// Windows 内核对象
private IntPtr _hTimer;
public void CreateTimer(int dueTime, int period)
{
// 1. 创建可等待定时器对象
_hTimer = CreateWaitableTimer(null, false, null);
// 2. 设置定时器
LARGE_INTEGER liDueTime;
liDueTime.QuadPart = -dueTime * 10000; // 转换为100ns单位
SetWaitableTimer(_hTimer, &liDueTime, period, null, null, false);
// 3. 关联到线程池的完成端口
ThreadPool.RegisterWaitForSingleObject(
_hTimer, // 等待的句柄
Callback, // 触发时的回调
null, // 状态参数
uint.MaxValue, // 超时时间
false // 是否一次性
);
}
}
sequenceDiagram participant App as 应用程序 participant Timer as Timer对象 participant OS as 操作系统 participant TP as 线程池 participant Callback as 回调函数 App->>Timer: new Timer(5000) Timer->>OS: 创建内核定时器 Note over OS: 定时器添加到系统队列 loop 每个系统时钟中断 OS->>OS: 检查所有定时器 OS->>OS: 计算最小到期时间 end Note over OS: 5秒后... OS->>TP: 定时器到期,投递到IO完成端口 TP->>TP: 从线程池获取可用线程 TP->>Callback: 在线程池线程中执行 Callback-->>App: 执行用户代码
// 时钟中断频率(概念性说明)
class ClockInterruptDemo
{
/*
* Windows 默认时钟中断频率:
* - 通常:每秒 64 次(每 15.625 毫秒一次)
* - 多媒体应用:可能提高到每秒 1000 次
*
* 这意味着定时器的精度受限于时钟中断频率
*/
public static void TimerPrecision()
{
var timer = new Timer(_ =>
{
// 如果设置 1ms 触发,实际可能 15ms 后才触发
Console.WriteLine($"理论精度:1ms,实际精度受系统时钟限制");
}, null, 1, Timeout.Infinite);
}
}
现代操作系统和 .NET 使用时间轮算法来高效管理大量定时器:
// 时间轮算法简化示例
class TimeWheel
{
/*
* 时间轮工作原理:
*
* 假设有 1000 个定时器,如果每个都单独监控,效率很低
* 时间轮将时间分成槽位(如 256 个槽)
* 每个槽代表一个时间间隔
*
* 槽0: [0-10ms] 的定时器
* 槽1: [10-20ms] 的定时器
* 槽2: [20-30ms] 的定时器
* ...
*
* 指针每秒移动,只检查当前槽的定时器
* 时间复杂度从 O(n) 降到 O(1)
*/
private List<Timer>[] _slots = new List<Timer>[256];
private int _currentSlot = 0;
public void AddTimer(Timer timer, int delayMs)
{
int slot = (delayMs / 10 + _currentSlot) % 256;
_slots[slot].Add(timer);
}
public void Tick()
{
// 每次时钟中断,只检查当前槽
foreach(var timer in _slots[_currentSlot])
{
ThreadPool.QueueUserWorkItem(timer.Callback);
}
_slots[_currentSlot].Clear();
_currentSlot = (_currentSlot + 1) % 256;
}
}
[StructLayout(LayoutKind.Sequential)]
class Timer
{
// 内部字段(概念性)
private TimerHolder _timerHolder; // 持有实际定时器对象
private TimerCallback _callback; // 用户回调
private object _state; // 状态对象
private int _dueTime; // 延迟时间
private int _period; // 间隔时间
private bool _isDisposed; // 是否已释放
// 实际存储在内核中的信息
// - 定时器队列链接
// - 等待句柄
// - 关联的完成端口
}
// Timer 的实际存储(简化版)
class TimerQueue
{
// 全局定时器队列(单例)
private static TimerQueue s_instance;
// 使用最小堆管理所有定时器
private MinHeap<TimerNode> _timers;
// 下一个到期时间
private int _nextTick;
}
public class TimerComparison
{
/*
* System.Threading.Timer 与其他定时器的区别:
*
* 1. System.Timers.Timer
* - 包装了 Threading.Timer
* - 提供更简单的组件模型
* - 支持 ISynchronizeInvoke(WinForms/WPF)
*
* 2. System.Windows.Forms.Timer
* - 基于 Windows 消息循环
* - UI 线程上执行,适合更新界面
* - 精度较低(约 55ms)
*
* 3. System.Web.UI.Timer
* - AJAX 定时器
* - 用于 ASP.NET 的异步回发
*
* 4. System.Threading.PeriodicTimer (.NET 6+)
* - 新的异步定时器
* - 支持 async/await 模式
*/
public async Task PeriodicTimerDemo()
{
// .NET 6+ 新的定时器
var timer = new PeriodicTimer(TimeSpan.FromSeconds(1));
while (await timer.WaitForNextTickAsync())
{
Console.WriteLine("每秒执行一次");
}
}
}
public class TimerPracticalDemo
{
private Timer _timer;
private int _executionCount;
private readonly object _lock = new object();
public void StartPreciseTimer()
{
// 高精度定时器(需要修改系统时钟精度)
// 注意:会影响系统功耗
Win32API.timeBeginPeriod(1); // 请求 1ms 精度
_timer = new Timer(PreciseCallback, null, 0, 1);
}
private void PreciseCallback(object state)
{
// 避免回调重入
if (!Monitor.TryEnter(_lock))
return;
try
{
var count = Interlocked.Increment(ref _executionCount);
var actualInterval = MeasureActualInterval();
// 实际间隔可能比请求的 1ms 大
if (actualInterval > 2)
{
Console.WriteLine($"警告:期望1ms,实际{actualInterval}ms");
}
}
finally
{
Monitor.Exit(_lock);
}
}
private double MeasureActualInterval()
{
// 测量实际执行间隔
return 0;
}
public void StopPreciseTimer()
{
_timer?.Dispose();
Win32API.timeEndPeriod(1); // 恢复系统时钟精度
}
}
// Windows API 声明
internal static class Win32API
{
[DllImport("winmm.dll")]
public static extern uint timeBeginPeriod(uint period);
[DllImport("winmm.dll")]
public static extern uint timeEndPeriod(uint period);
}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。