解决MySql布尔型新旧版本兼容问题,采用枚举来表示布尔型的数据表。由正向工程赋值
|
# 高级定时器TimerX
## 概述
`NewLife.Threading.TimerX` 是一个功能强大的定时器实现,相比系统 `System.Threading.Timer` 具有以下优势:
- **不可重入**:回调执行完毕后才开始计时下一次
- **支持异步回调**:原生支持 `async/await`
- **绝对时间执行**:支持固定时刻执行(如每天2点)
- **Cron 表达式**:支持复杂的定时规则
- **链路追踪**:内置 `ITracer` 埋点支持
- **安全释放**:挂载在静态调度器上,避免被GC提前回收
**命名空间**: `NewLife.Threading`
**源码**: [NewLife.Core/Threading/TimerX.cs](https://github.com/NewLifeX/X/blob/master/NewLife.Core/Threading/TimerX.cs)
---
## 快速入门
### 基础用法:周期性执行
```csharp
using NewLife.Threading;
// 1秒后首次执行,然后每5秒执行一次
var timer = new TimerX(state =>
{
Console.WriteLine($"执行时间:{DateTime.Now}");
}, null, 1000, 5000);
// 使用完毕记得释放
timer.Dispose();
```
**参数说明**:
- `state`:用户数据,会传递给回调函数
- `dueTime`:延迟时间(毫秒),首次执行前等待的时间
- `period`:间隔周期(毫秒),设为0或-1表示只执行一次
### 异步回调
```csharp
// 支持 async/await 的异步回调
var timer = new TimerX(async state =>
{
await Task.Delay(100); // 模拟异步操作
Console.WriteLine("异步任务完成");
}, null, 1000, 5000);
```
**自动设置**:
- 使用 `Func<Object, Task>` 时,自动设置 `IsAsyncTask = true` 和 `Async = true`
### 绝对时间执行
```csharp
// 每天凌晨2点执行
var start = DateTime.Today.AddHours(2);
var timer = new TimerX(state =>
{
Console.WriteLine("执行数据清理任务");
}, null, start, 24 * 3600 * 1000); // 周期24小时
```
**特点**:
- 如果 `startTime` 小于当前时间,会自动加 `period` 直到大于当前时间
- 自动设置 `Absolutely = true`
- 不受 `SetNext` 影响,始终在固定时刻执行
### Cron 表达式
```csharp
// 每个工作日早上9点执行
var timer = new TimerX(state =>
{
Console.WriteLine("工作日任务");
}, null, "0 0 9 * * 1-5");
// 支持多个Cron表达式,分号分隔
var timer2 = new TimerX(state =>
{
Console.WriteLine("混合任务");
}, null, "0 0 2 * * 1-5;0 0 3 * * 6"); // 工作日2点,周六3点
```
**自动设置**:
- 使用 Cron 表达式时,自动设置 `Absolutely = true`
- 下一次执行时间由 Cron 计算
---
## 核心特性
### 1. 不可重入机制
系统 `Timer` 的问题:
```csharp
// System.Threading.Timer 的问题
var timer = new Timer(_ =>
{
Thread.Sleep(3000); // 假设任务耗时3秒
Console.WriteLine("执行");
}, null, 0, 1000); // 每1秒触发
// 结果:可能同时有多个回调在执行,造成并发问题!
```
TimerX 的解决方案:
```csharp
var timer = new TimerX(_ =>
{
Thread.Sleep(3000); // 耗时3秒
Console.WriteLine("执行");
}, null, 0, 1000);
// 执行流程:
// 0秒:开始第1次执行
// 3秒:第1次执行完毕,等待1秒
// 4秒:开始第2次执行
// 7秒:第2次执行完毕,等待1秒
// 8秒:开始第3次执行
// ...
```
**原理**:回调执行完毕后才开始计算下一次的延迟时间,确保同一时刻只有一个回调在执行。
### 2. 基于 TickCount 的精准计时
TimerX 使用 `Runtime.TickCount64` 作为计时基准,无惧系统时间回拨:
```csharp
// 即使手动调整系统时间,TimerX 也不受影响
var timer = new TimerX(_ =>
{
Console.WriteLine($"执行:{DateTime.Now}");
}, null, 0, 5000);
```
**优势**:
- 使用开机嘀嗒数而非系统时钟
- 不受夏令时、时区调整影响
- 每次 `SetNextTick` 会刷新时间基准,自动修正漂移
### 3. 绝对时间与 Cron
- **`Absolutely = true`**:表示绝对时间执行,不受 `SetNext` 影响
- **Cron 构造函数**:自动设置 `Absolutely = true`,通过 `Cron.GetNext(now)` 计算下一次执行时间
```csharp
// 绝对时间定时器
var timer = new TimerX(_ => { }, null, DateTime.Today.AddHours(2), 24 * 3600 * 1000);
timer.Absolutely; // true
// Cron 定时器
var timer2 = new TimerX(_ => { }, null, "0 0 2 * * *");
timer2.Absolutely; // true
timer2.Crons; // Cron数组
```
---
## 构造函数详解
### 1. 普通周期定时器(同步)
```csharp
public TimerX(TimerCallback callback, Object? state, Int32 dueTime, Int32 period, String? scheduler = null)
```
**参数**:
- `callback`: 回调委托 `void Callback(Object state)`
- `state`: 用户数据
- `dueTime`: 延迟时间(毫秒),首次执行前等待
- `period`: 间隔周期(毫秒),0或-1表示只执行一次
- `scheduler`: 调度器名称,默认使用 `TimerScheduler.Default`
**示例**:
```csharp
var timer = new TimerX(_ =>
{
Console.WriteLine("执行");
}, null, 1000, 5000);
```
### 2. 异步周期定时器
```csharp
public TimerX(Func<Object, Task> callback, Object? state, Int32 dueTime, Int32 period, String? scheduler = null)
```
**参数**:
- `callback`: 异步回调委托 `async Task Callback(Object state)`
- 其他参数同上
**自动设置**:
- `IsAsyncTask = true`
- `Async = true`
**示例**:
```csharp
var timer = new TimerX(async _ =>
{
await DoWorkAsync();
}, null, 1000, 5000);
```
### 3. 绝对时间定时器(同步)
```csharp
public TimerX(TimerCallback callback, Object? state, DateTime startTime, Int32 period, String? scheduler = null)
```
**参数**:
- `startTime`: 绝对开始时间,指定时刻执行
- `period`: 间隔周期(毫秒),必须大于0
**自动设置**:
- `Absolutely = true`
- 如果 `startTime` 小于当前时间,自动加 `period` 对齐
**示例**:
```csharp
// 每天凌晨2点执行
var start = DateTime.Today.AddHours(2);
var timer = new TimerX(_ =>
{
Console.WriteLine("凌晨任务");
}, null, start, 24 * 3600 * 1000);
```
### 4. 绝对时间定时器(异步)
```csharp
public TimerX(Func<Object, Task> callback, Object? state, DateTime startTime, Int32 period, String? scheduler = null)
```
**自动设置**:
- `IsAsyncTask = true`
- `Async = true`
- `Absolutely = true`
### 5. Cron 定时器(同步)
```csharp
public TimerX(TimerCallback callback, Object? state, String cronExpression, String? scheduler = null)
```
**参数**:
- `cronExpression`: Cron 表达式,支持分号分隔多个表达式
**自动设置**:
- `Absolutely = true`
- 解析表达式并保存到 `_crons` 数组
**示例**:
```csharp
// 每个工作日早上9点
var timer = new TimerX(_ =>
{
Console.WriteLine("工作日任务");
}, null, "0 0 9 * * 1-5");
// 多个时间点
var timer2 = new TimerX(_ =>
{
Console.WriteLine("混合任务");
}, null, "0 0 2 * * 1-5;0 0 3 * * 6");
```
### 6. Cron 定时器(异步)
```csharp
public TimerX(Func<Object, Task> callback, Object? state, String cronExpression, String? scheduler = null)
```
**自动设置**:
- `IsAsyncTask = true`
- `Async = true`
- `Absolutely = true`
---
## 核心属性
| 属性 | 类型 | 说明 |
|-----|------|------|
| `Id` | Int32 | 定时器唯一标识,自动分配 |
| `Period` | Int32 | 间隔周期(毫秒),0或-1表示只执行一次 |
| `Async` | Boolean | 是否异步执行,默认 false |
| `Absolutely` | Boolean | 是否绝对精确时间执行,默认 false |
| `Calling` | Boolean | 回调是否正在执行(只读) |
| `Timers` | Int32 | 累计调用次数(只读) |
| `Cost` | Int32 | 平均耗时(毫秒,只读) |
| `NextTime` | DateTime | 下一次调用时间(只读) |
| `NextTick` | Int64 | 下一次执行时间的嘀嗒数(只读) |
| `Crons` | Cron[] | Cron 表达式集合(只读) |
| `Tracer` | ITracer | 链路追踪器 |
| `TracerName` | String | 链路追踪名称,默认 `timer:{方法名}` |
| `State` | Object | 用户数据,弱引用存储 |
| `Scheduler` | TimerScheduler | 所属调度器 |
---
## 核心方法
### SetNext - 设置下一次执行时间
```csharp
/// <summary>设置下一次运行时间</summary>
/// <param name="ms">延迟毫秒数。小于等于0表示马上调度</param>
public void SetNext(Int32 ms)
```
**示例**:
```csharp
var timer = new TimerX(_ =>
{
Console.WriteLine("执行");
// 动态调整下一次执行时间
if (someCondition)
timer.SetNext(10000); // 10秒后执行
else
timer.SetNext(1000); // 1秒后执行
}, null, 1000, 5000);
```
**注意**:
- 对于 `Absolutely = true` 的定时器(绝对时间、Cron),`SetNext` 无效
- 调用后会唤醒调度器立即检查
### Change - 更改计时器参数
```csharp
/// <summary>更改计时器的启动时间和方法调用之间的时间间隔</summary>
/// <param name="dueTime">延迟时间</param>
/// <param name="period">间隔周期</param>
/// <returns>是否成功更改</returns>
public Boolean Change(TimeSpan dueTime, TimeSpan period)
```
**示例**:
```csharp
var timer = new TimerX(_ =>
{
Console.WriteLine("执行");
}, null, 1000, 5000);
// 修改为2秒后首次执行,然后每10秒一次
timer.Change(TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(10));
```
**限制**:
- 对于 `Absolutely = true` 的定时器,返回 false,修改失败
- 对于 Cron 定时器,返回 false,修改失败
- `period` 为负数或 Infinite 时,定时器会被销毁
### Dispose - 销毁定时器
```csharp
/// <summary>销毁定时器</summary>
public void Dispose()
```
**示例**:
```csharp
var timer = new TimerX(_ =>
{
Console.WriteLine("执行");
}, null, 1000, 5000);
// 使用完毕释放
timer.Dispose();
```
**重要性**:
- TimerX 挂载在静态调度器 `TimerScheduler` 上
- **必须手动调用 `Dispose()`** 才能从调度器移除
- 否则会造成内存泄漏和定时器继续执行
---
## 静态方法
### Delay - 延迟执行
```csharp
/// <summary>延迟执行一个委托</summary>
/// <param name="callback">回调委托</param>
/// <param name="ms">延迟毫秒数</param>
/// <returns>定时器实例</returns>
public static TimerX Delay(TimerCallback callback, Int32 ms)
```
**示例**:
```csharp
// 10秒后执行一次,然后销毁
var timer = TimerX.Delay(_ =>
{
Console.WriteLine("延迟任务执行");
}, 10000);
// 注意:委托可能还没执行,timer对象就被GC回收了
// 建议保持timer引用
```
**注意**:
- 自动设置 `Async = true`
- 仅执行一次(Period=0)
- **警告**:如果不保持 `timer` 引用,可能被 GC 回收导致未执行
### Now - 缓存的当前时间
```csharp
/// <summary>当前时间。定时读取系统时间,避免频繁读取造成性能瓶颈</summary>
public static DateTime Now { get; }
```
**示例**:
```csharp
var now = TimerX.Now; // 性能优于 DateTime.Now
Console.WriteLine(now);
```
**原理**:
- 每500毫秒更新一次系统时间
- 避免频繁调用 `DateTime.Now` 造成性能瓶颈
- 适用于对时间精度要求不高的场景(±500ms)
---
## 调度器 TimerScheduler
TimerX 依赖 `TimerScheduler` 进行统一调度。
### 默认调度器
```csharp
var timer = new TimerX(_ => { }, null, 1000, 5000);
timer.Scheduler; // TimerScheduler.Default
```
### 自定义调度器
```csharp
// 创建专属调度器
var timer = new TimerX(_ =>
{
Console.WriteLine("执行");
}, null, 1000, 5000, "MyScheduler");
timer.Scheduler.Name; // "MyScheduler"
```
**适用场景**:
- 需要独立的调度线程池
- 隔离不同业务的定时器
- 控制不同调度器的优先级
---
## 链路追踪
TimerX 内置链路追踪支持,自动为每次执行创建 Span。
### 设置追踪器
```csharp
using NewLife.Log;
var timer = new TimerX(_ =>
{
Console.WriteLine("执行");
}, null, 1000, 5000)
{
Tracer = DefaultTracer.Instance, // 设置追踪器
TracerName = "MyTask" // 自定义埋点名称
};
```
### 追踪数据
每次定时器触发,会创建一个 Span:
- **名称**:默认为 `timer:{方法名}`,可通过 `TracerName` 自定义
- **标签**:记录定时器ID、周期等信息
- **耗时**:记录每次回调执行的时间
**查看统计**:
```
Tracer[timer:DoWork] Total=100 Errors=0 Speed=0.02tps Cost=150ms MaxCost=200ms MinCost=100ms
```
详见:[链路追踪ITracer](/NewLife/X/Blob/master/Doc/tracer-链路追踪ITracer.md)
---
## 使用场景
### 1. 定期数据清理
```csharp
// 每天凌晨3点清理过期数据
var timer = new TimerX(state =>
{
var days = (Int32)state!;
var before = DateTime.Now.AddDays(-days);
var count = Database.Delete("WHERE CreateTime < @time", new { time = before });
Console.WriteLine($"清理了 {count} 条过期数据");
}, 30, DateTime.Today.AddHours(3), 24 * 3600 * 1000);
```
### 2. 心跳检测
```csharp
var timer = new TimerX(_ =>
{
foreach (var client in clients)
{
if (client.LastActive.AddMinutes(5) < DateTime.Now)
{
Console.WriteLine($"客户端 {client.Id} 超时,断开连接");
client.Disconnect();
}
}
}, null, 0, 60000); // 每分钟检查一次
```
### 3. 定期数据同步
```csharp
var timer = new TimerX(async _ =>
{
var data = await FetchDataFromApiAsync();
await SaveToDatabase(data);
Console.WriteLine("数据同步完成");
}, null, 0, 300000); // 每5分钟同步一次
```
### 4. 工作日定时报表
```csharp
// 每个工作日早上9点生成报表
var timer = new TimerX(_ =>
{
var report = GenerateReport(DateTime.Today.AddDays(-1));
SendEmail(report);
Console.WriteLine("报表已发送");
}, null, "0 0 9 * * 1-5");
```
### 5. 定期健康检查
```csharp
var timer = new TimerX(_ =>
{
var healthy = CheckSystemHealth();
if (!healthy)
{
SendAlert("系统异常");
}
}, null, 0, 30000); // 每30秒检查一次
```
---
## 最佳实践
### 1. 异步回调优先
对于耗时操作,使用异步回调避免阻塞:
```csharp
// 推荐
var timer = new TimerX(async _ =>
{
await DoHeavyWorkAsync();
}, null, 0, 5000);
// 不推荐
var timer2 = new TimerX(_ =>
{
DoHeavyWork(); // 阻塞线程
}, null, 0, 5000);
```
### 2. 主动释放资源
```csharp
public class MyService : IDisposable
{
private readonly TimerX _timer;
public MyService()
{
_timer = new TimerX(_ => DoWork(), null, 0, 5000);
}
public void Dispose()
{
_timer?.Dispose(); // 释放定时器
}
}
```
### 3. 使用 Current 属性
在回调中可以访问当前定时器:
```csharp
var timer = new TimerX(_ =>
{
var current = TimerX.Current;
Console.WriteLine($"定时器[{current.Id}]第{current.Timers}次执行");
// 动态调整周期
if (current.Timers > 10)
current.Period = 10000;
}, null, 0, 5000);
```
### 4. 错误处理
```csharp
var timer = new TimerX(_ =>
{
try
{
DoWork();
}
catch (Exception ex)
{
Console.WriteLine($"定时任务异常:{ex.Message}");
// 记录日志,不要抛出异常
}
}, null, 0, 5000);
```
### 5. Cron 复杂场景
```csharp
// 工作日早上9点,周六早上10点
var crons = "0 0 9 * * 1-5;0 0 10 * * 6";
var timer = new TimerX(_ =>
{
Console.WriteLine("执行任务");
}, null, crons);
```
---
## 注意事项
### 1. 必须主动释放
```csharp
var timer = new TimerX(_ => { }, null, 0, 5000);
// ... 使用 ...
timer.Dispose(); // 必须调用,否则内存泄漏
```
### 2. Delay 方法的陷阱
```csharp
// 错误:timer可能被GC回收
TimerX.Delay(_ => Console.WriteLine("执行"), 10000);
// 正确:保持引用
var timer = TimerX.Delay(_ => Console.WriteLine("执行"), 10000);
// ... 保持timer在作用域内 ...
```
### 3. 绝对时间不可修改
```csharp
var timer = new TimerX(_ => { }, null, DateTime.Today.AddHours(2), 86400000);
timer.SetNext(1000); // 无效
timer.Change(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5)); // 返回 false
```
### 4. State 是弱引用
```csharp
var obj = new MyObject();
var timer = new TimerX(_ =>
{
var state = _.State as MyObject; // 可能为 null
}, obj, 0, 5000);
// 如果 obj 被 GC 回收,State 会变为 null
```
### 5. 周期为0只执行一次
```csharp
var timer = new TimerX(_ =>
{
Console.WriteLine("只执行一次");
}, null, 1000, 0); // Period=0,只执行一次
```
---
## 常见问题
### 1. 定时器不执行?
检查:
- 是否被 GC 回收(保持引用)
- 是否已 Dispose
- Period 是否为负数
- Cron 表达式是否正确
### 2. 定时器执行了多次?
TimerX 是不可重入的,不会出现这个问题。如果出现,检查是否创建了多个定时器实例。
### 3. 如何停止定时器?
```csharp
timer.Dispose(); // 销毁并移除
```
### 4. 如何暂停和恢复?
```csharp
// 暂停(设置为只执行一次)
timer.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);
// 恢复
timer.Change(TimeSpan.Zero, TimeSpan.FromSeconds(5));
```
### 5. Cron 和绝对时间的区别?
| 特性 | Cron | 绝对时间 |
|-----|------|---------|
| 表达式 | 支持复杂规则 | 固定时刻+周期 |
| 灵活性 | 高(秒级、星期等) | 低(固定周期) |
| 性能 | 略低(需解析) | 高 |
| 适用场景 | 复杂定时 | 简单周期 |
---
## 参考资料
- **Cron 文档**: [cron-Cron表达式.md](/NewLife/X/Blob/master/Doc/cron-Cron表达式.md)
- **链路追踪**: [tracer-链路追踪ITracer.md](/NewLife/X/Blob/master/Doc/tracer-链路追踪ITracer.md)
- **在线文档**: https://newlifex.com/core/timerx
- **源码**: https://github.com/NewLifeX/X/blob/master/NewLife.Core/Threading/TimerX.cs
---
## 更新日志
- **2025-01**: 完善文档,补充详细示例和最佳实践
- **2024**: 支持 .NET 9.0,优化性能
- **2023**: 增加 Cron 多表达式支持
- **2022**: 增加异步回调支持
- **2020**: 初始版本,基于 TickCount 的精准计时
|