TimerX 整个代码
using System.Reflection;
using NewLife.Log;
namespace NewLife.Threading;
/// <summary>不可重入的定时器,支持Cron</summary>
/// <remarks>
/// 文档 https://newlifex.com/core/timerx
///
/// 为了避免系统的Timer可重入的问题,差别在于本地调用完成后才开始计算时间间隔。这实际上也是经常用到的。
///
/// 因为挂载在静态列表上,必须从外部主动调用<see cref="IDisposable.Dispose"/>才能销毁定时器。
/// 但是要注意GC回收定时器实例。
///
/// 该定时器不能放入太多任务,否则适得其反!
///
/// TimerX必须维持对象,否则Scheduler也没有维持对象时,大家很容易一起被GC回收。
/// </remarks>
public class TimerX : ITimer, IDisposable
{
#region 属性
/// <summary>编号</summary>
public Int32 Id { get; internal set; }
/// <summary>所属调度器</summary>
public TimerScheduler Scheduler { get; private set; }
/// <summary>目标对象。弱引用,使得调用方对象可以被GC回收</summary>
internal readonly WeakReference Target;
/// <summary>委托方法</summary>
internal readonly MethodInfo Method;
internal readonly Boolean IsAsyncTask;
private WeakReference? _state;
/// <summary>获取/设置 用户数据</summary>
public Object? State
{
get => _state != null && _state.IsAlive ? _state.Target : null;
set
{
if (_state == null)
_state = new WeakReference(value);
else
_state.Target = value;
}
}
/// <summary>基准时间。开机时间</summary>
private static DateTime _baseTime;
private Int64 _nextTick;
/// <summary>下一次执行时间。开机以来嘀嗒数,无惧时间回拨问题</summary>
public Int64 NextTick => _nextTick;
/// <summary>获取/设置 下一次调用时间</summary>
public DateTime NextTime => _baseTime.AddMilliseconds(_nextTick);
/// <summary>获取/设置 调用次数</summary>
public Int32 Timers { get; internal set; }
/// <summary>获取/设置 间隔周期。毫秒,设为0或-1则只调用一次</summary>
public Int32 Period { get; set; }
/// <summary>获取/设置 异步执行任务。默认false</summary>
public Boolean Async { get; set; }
/// <summary>获取/设置 绝对精确时间执行。默认false</summary>
public Boolean Absolutely { get; set; }
/// <summary>调用中</summary>
public Boolean Calling { get; internal set; }
/// <summary>平均耗时。毫秒</summary>
public Int32 Cost { get; internal set; }
/// <summary>判断任务是否执行的委托。一般跟异步配合使用,避免频繁从线程池借出线程</summary>
[Obsolete("该委托容易造成内存泄漏,故取消", true)]
public Func<Boolean>? CanExecute { get; set; }
/// <summary>Cron表达式,实现复杂的定时逻辑</summary>
public Cron[]? Crons => _crons;
/// <summary>Cron表达式,实现复杂的定时逻辑</summary>
[Obsolete("=>Crons")]
public Cron? Cron => _crons?.FirstOrDefault();
/// <summary>链路追踪。追踪每一次定时事件</summary>
public ITracer? Tracer { get; set; }
/// <summary>链路追踪名称。默认使用方法名</summary>
public String TracerName { get; set; }
private DateTime _AbsolutelyNext;
private readonly Cron[]? _crons;
#endregion
#region 静态
#if NET45
private static readonly ThreadLocal<TimerX?> _Current = new();
#else
private static readonly AsyncLocal<TimerX?> _Current = new();
#endif
/// <summary>当前定时器</summary>
public static TimerX? Current { get => _Current.Value; set => _Current.Value = value; }
#endregion
#region 构造
private TimerX(Object? target, MethodInfo method, Object? state, String? scheduler = null)
{
Target = new WeakReference(target);
Method = method;
State = state;
// 使用开机滴答作为定时调度基准
_nextTick = Runtime.TickCount64;
//_baseTime = DateTime.Now.AddMilliseconds(-_nextTick);
Scheduler = (scheduler == null || scheduler.IsNullOrEmpty()) ? TimerScheduler.Default : TimerScheduler.Create(scheduler);
//Scheduler.Add(this);
_baseTime = Scheduler.GetNow().AddMilliseconds(-_nextTick);
TracerName = $"timer:{method.Name}";
}
private void Init(Int64 ms)
{
SetNextTick(ms);
Scheduler.Add(this);
}
/// <summary>实例化一个不可重入的定时器</summary>
/// <param name="callback">委托</param>
/// <param name="state">用户数据</param>
/// <param name="dueTime">多久之后开始。毫秒</param>
/// <param name="period">间隔周期。毫秒</param>
/// <param name="scheduler">调度器</param>
public TimerX(TimerCallback callback, Object? state, Int32 dueTime, Int32 period, String? scheduler = null) : this(callback.Target, callback.Method, state, scheduler)
{
if (callback == null) throw new ArgumentNullException(nameof(callback));
if (dueTime < 0) throw new ArgumentOutOfRangeException(nameof(dueTime));
Period = period;
Init(dueTime);
}
/// <summary>实例化一个不可重入的定时器</summary>
/// <param name="callback">委托</param>
/// <param name="state">用户数据</param>
/// <param name="dueTime">多久之后开始。毫秒</param>
/// <param name="period">间隔周期。毫秒</param>
/// <param name="scheduler">调度器</param>
public TimerX(Func<Object, Task> callback, Object? state, Int32 dueTime, Int32 period, String? scheduler = null) : this(callback.Target, callback.Method, state, scheduler)
{
if (callback == null) throw new ArgumentNullException(nameof(callback));
if (dueTime < 0) throw new ArgumentOutOfRangeException(nameof(dueTime));
IsAsyncTask = true;
Async = true;
Period = period;
Init(dueTime);
}
/// <summary>实例化一个绝对定时器,指定时刻执行,跟当前时间和SetNext无关</summary>
/// <param name="callback">委托</param>
/// <param name="state">用户数据</param>
/// <param name="startTime">绝对开始时间</param>
/// <param name="period">间隔周期。毫秒</param>
/// <param name="scheduler">调度器</param>
public TimerX(TimerCallback callback, Object? state, DateTime startTime, Int32 period, String? scheduler = null) : this(callback.Target, callback.Method, state, scheduler)
{
if (callback == null) throw new ArgumentNullException(nameof(callback));
if (startTime <= DateTime.MinValue) throw new ArgumentOutOfRangeException(nameof(startTime));
if (period <= 0) throw new ArgumentOutOfRangeException(nameof(period));
Period = period;
Absolutely = true;
//var now = DateTime.Now;
var now = Scheduler.GetNow();
var next = startTime;
while (next < now) next = next.AddMilliseconds(period);
var ms = (Int64)(next - now).TotalMilliseconds;
_AbsolutelyNext = next;
Init(ms);
}
/// <summary>实例化一个绝对定时器,指定时刻执行,跟当前时间和SetNext无关</summary>
/// <param name="callback">委托</param>
/// <param name="state">用户数据</param>
/// <param name="startTime">绝对开始时间</param>
/// <param name="period">间隔周期。毫秒</param>
/// <param name="scheduler">调度器</param>
public TimerX(Func<Object, Task> callback, Object? state, DateTime startTime, Int32 period, String? scheduler = null) : this(callback.Target, callback.Method, state, scheduler)
{
if (callback == null) throw new ArgumentNullException(nameof(callback));
if (startTime <= DateTime.MinValue) throw new ArgumentOutOfRangeException(nameof(startTime));
if (period <= 0) throw new ArgumentOutOfRangeException(nameof(period));
IsAsyncTask = true;
Async = true;
Period = period;
Absolutely = true;
//var now = DateTime.Now;
var now = Scheduler.GetNow();
var next = startTime;
while (next < now) next = next.AddMilliseconds(period);
var ms = (Int64)(next - now).TotalMilliseconds;
_AbsolutelyNext = next;
Init(ms);
}
/// <summary>实例化一个Cron定时器</summary>
/// <param name="callback">委托</param>
/// <param name="state">用户数据</param>
/// <param name="cronExpression">Cron表达式。支持多个表达式,分号分隔</param>
/// <param name="scheduler">调度器</param>
public TimerX(TimerCallback callback, Object? state, String cronExpression, String? scheduler = null) : this(callback.Target, callback.Method, state, scheduler)
{
if (callback == null) throw new ArgumentNullException(nameof(callback));
if (cronExpression.IsNullOrEmpty()) throw new ArgumentNullException(nameof(cronExpression));
var list = new List<Cron>();
foreach (var item in cronExpression.Split(";"))
{
var cron = new Cron();
if (!cron.Parse(item)) throw new ArgumentException($"Invalid Cron expression[{item}]", nameof(cronExpression));
list.Add(cron);
}
_crons = list.ToArray();
Absolutely = true;
//var now = DateTime.Now;
var now = Scheduler.GetNow();
var next = _crons.Min(e => e.GetNext(now));
var ms = (Int64)(next - now).TotalMilliseconds;
_AbsolutelyNext = next;
Init(ms);
//Init(_AbsolutelyNext = _cron.GetNext(DateTime.Now));
}
/// <summary>实例化一个Cron定时器</summary>
/// <param name="callback">委托</param>
/// <param name="state">用户数据</param>
/// <param name="cronExpression">Cron表达式。支持多个表达式,分号分隔</param>
/// <param name="scheduler">调度器</param>
public TimerX(Func<Object, Task> callback, Object? state, String cronExpression, String? scheduler = null) : this(callback.Target, callback.Method, state, scheduler)
{
if (callback == null) throw new ArgumentNullException(nameof(callback));
if (cronExpression.IsNullOrEmpty()) throw new ArgumentNullException(nameof(cronExpression));
var list = new List<Cron>();
foreach (var item in cronExpression.Split(";"))
{
var cron = new Cron();
if (!cron.Parse(item)) throw new ArgumentException($"Invalid Cron expression[{item}]", nameof(cronExpression));
list.Add(cron);
}
_crons = list.ToArray();
IsAsyncTask = true;
Async = true;
Absolutely = true;
//var now = DateTime.Now;
var now = Scheduler.GetNow();
var next = _crons.Min(e => e.GetNext(now));
var ms = (Int64)(next - now).TotalMilliseconds;
_AbsolutelyNext = next;
Init(ms);
//Init(_AbsolutelyNext = _cron.GetNext(DateTime.Now));
}
/// <summary>销毁定时器</summary>
public void Dispose()
{
Dispose(true);
// 告诉GC,不要调用析构函数
GC.SuppressFinalize(this);
}
/// <summary>销毁</summary>
/// <param name="disposing"></param>
protected virtual void Dispose(Boolean disposing)
{
if (disposing)
{
// 释放托管资源
}
// 释放非托管资源
Scheduler?.Remove(this, disposing ? "Dispose" : "GC");
}
#if NET5_0_OR_GREATER
/// <summary>异步销毁</summary>
/// <returns></returns>
public ValueTask DisposeAsync()
{
Dispose();
return ValueTask.CompletedTask;
}
#endif
#endregion
#region 方法
/// <summary>是否已设置下一次时间</summary>
internal Boolean hasSetNext;
private void SetNextTick(Int64 ms)
{
// 使用开机滴答来做定时调度,无惧时间回拨,每次修正时间基准
var tick = Runtime.TickCount64;
_baseTime = Scheduler.GetNow().AddMilliseconds(-tick);
_nextTick = tick + ms;
}
/// <summary>设置下一次运行时间</summary>
/// <param name="ms">小于等于0表示马上调度</param>
public void SetNext(Int32 ms)
{
//NextTime = DateTime.Now.AddMilliseconds(ms);
SetNextTick(ms);
hasSetNext = true;
Scheduler.Wake();
}
/// <summary>设置下一次执行时间,并获取间隔</summary>
/// <returns>返回下一次执行的间隔时间,不能小于等于0,否则定时器被销毁</returns>
internal Int32 SetAndGetNextTime()
{
// 如果已设置
var period = Period;
var nowTick = Runtime.TickCount64;
if (hasSetNext)
{
var ts = (Int32)(_nextTick - nowTick);
return ts > 0 ? ts : period;
}
if (Absolutely)
{
// Cron以当前时间开始计算下一次
// 绝对时间还没有到时,不计算下一次
//var now = DateTime.Now;
var now = Scheduler.GetNow();
DateTime next;
if (_crons != null)
{
next = _crons.Min(e => e.GetNext(now));
// 如果cron计算得到的下一次时间过近,则需要重新计算
if ((next - now).TotalMilliseconds < 1000) next = _crons.Min(e => e.GetNext(next));
}
else
{
// 能够处理基准时间变大,但不能处理基准时间变小
next = _AbsolutelyNext;
while (next < now) next = next.AddMilliseconds(period);
}
// 即使基准时间改变,也不影响绝对时间定时器的执行时刻
_AbsolutelyNext = next;
var ts = (Int32)Math.Round((next - now).TotalMilliseconds);
SetNextTick(ts);
return ts > 0 ? ts : period;
}
else
{
//NextTime = DateTime.Now.AddMilliseconds(period);
SetNextTick(period);
return period;
}
}
/// <summary>更改计时器的启动时间和方法调用之间的时间间隔,使用 TimeSpan 值度量时间间隔。</summary>
/// <param name="dueTime">一个 TimeSpan,表示在调用构造 ITimer 时指定的回调方法之前的延迟时间量。 指定 InfiniteTimeSpan 可防止重新启动计时器。 指定 Zero 可立即重新启动计时器。</param>
/// <param name="period">构造 Timer 时指定的回调方法调用之间的时间间隔。 指定 InfiniteTimeSpan 可以禁用定期终止。</param>
/// <returns></returns>
public Boolean Change(TimeSpan dueTime, TimeSpan period)
{
if (Absolutely) return false;
if (Crons != null && Crons.Length > 0) return false;
if (period.TotalMilliseconds <= 0)
{
Dispose();
return true;
}
Period = (Int32)period.TotalMilliseconds;
if (dueTime.TotalMilliseconds >= 0) SetNext((Int32)dueTime.TotalMilliseconds);
return true;
}
#endregion
#region 静态方法
/// <summary>延迟执行一个委托。特别要小心,很可能委托还没被执行,对象就被gc回收了</summary>
/// <param name="callback"></param>
/// <param name="ms"></param>
/// <returns></returns>
public static TimerX Delay(TimerCallback callback, Int32 ms) => new(callback, null, ms, 0) { Async = true };
private static TimerX? _NowTimer;
private static DateTime _Now;
/// <summary>当前时间。定时读取系统时间,避免频繁读取系统时间造成性能瓶颈</summary>
public static DateTime Now
{
get
{
if (_NowTimer == null)
{
lock (TimerScheduler.Default)
{
if (_NowTimer == null)
{
// 多线程下首次访问Now可能取得空时间
_Now = TimerScheduler.Default.GetNow();
_NowTimer = new TimerX(CopyNow, null, 0, 500);
}
}
}
return _Now;
}
}
private static void CopyNow(Object? state) => _Now = TimerScheduler.Default.GetNow();
#endregion
#region 辅助
/// <summary>已重载</summary>
/// <returns></returns>
public override String ToString() => $"[{Id}]{Method.DeclaringType?.Name}.{Method.Name} ({(_crons != null ? _crons.Join(";") : (Period + "ms"))})";
#endregion
}
看这个代码最懵的是这个字段,
public Boolean Async { get; set; }
整个代码里没看到调用,于是整个没有看懂。
后面点击引用才发现TimerScheduler里面有调用,于是全部看懂了。
else if (!timer.Async)//
/// <summary>调度主程序</summary>
/// <param name="state"></param>
private void Process(Object? state)
{
Current = this;
while (true)
{
// 准备好定时器列表
var arr = Timers;
// 如果没有任务,则销毁线程
if (arr.Length == 0 && _period == 60_000)
{
WriteLog("没有可用任务,销毁线程");
var th = thread;
thread = null;
//th?.Abort();
break;
}
try
{
var now = Runtime.TickCount64;
// 设置一个较大的间隔,内部会根据处理情况调整该值为最合理值
_period = 60_000;
foreach (var timer in arr)
{
if (!timer.Calling && CheckTime(timer, now))
{
// 必须在主线程设置状态,否则可能异步线程还没来得及设置开始状态,主线程又开始了新的一轮调度
timer.Calling = true;
if (timer.IsAsyncTask)
Task.Factory.StartNew(ExecuteAsync, timer);
else if (!timer.Async)//这里调用
Execute(timer);
else
//Task.Factory.StartNew(() => ProcessItem(timer));
// 不需要上下文流动,捕获所有异常
ThreadPool.UnsafeQueueUserWorkItem(s =>
{
try
{
Execute(s);
}
catch (Exception ex)
{
XTrace.WriteException(ex);
}
}, timer);
}
}
}
catch (ThreadAbortException) { break; }
catch (ThreadInterruptedException) { break; }
catch { }
_waitForTimer ??= new AutoResetEvent(false);
if (_period > 0) _waitForTimer.WaitOne(_period, true);
}
}
在这个代码里还有一些基础知识,以前真的没有用心,现在又重新学习一下。this这个构造函数重载。
问了ai
以下是一个 C# 中构造函数 this 多次重载的示例:
using System;
class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string Address { get; set; }
// 无参构造函数
public Person()
{
Name = "Unknown";
Age = 0;
Address = "Unknown";
}
// 接受姓名和年龄的构造函数
public Person(string name, int age) : this()
{
Name = name;
Age = age;
}
// 接受姓名、年龄和地址的构造函数
public Person(string name, int age, string address) : this(name, age)
{
Address = address;
}
}
class Program
{
static void Main()
{
// 使用无参构造函数创建对象
Person person1 = new Person();
Console.WriteLine($"Person 1: Name = {person1.Name}, Age = {person1.Age}, Address = {person1.Address}");
// 使用接受姓名和年龄的构造函数创建对象
Person person2 = new Person("Alice", 30);
Console.WriteLine($"Person 2: Name = {person2.Name}, Age = {person2.Age}, Address = {person2.Address}");
// 使用接受姓名、年龄和地址的构造函数创建对象
Person person3 = new Person("Bob", 25, "123 Main St");
Console.WriteLine($"Person 3: Name = {person3.Name}, Age = {person3.Age}, Address = {person3.Address}");
}
}
代码解释:
-
类的定义:
-
Person类包含三个属性:Name、Age和Address。 -
还有三个构造函数,分别为:
-
无参构造函数:
public Person():将Name、Age和Address属性初始化为默认值。
-
接受姓名和年龄的构造函数:
-
public Person(string name, int age) : this():- 使用
: this()调用无参构造函数,先将Name、Age和Address属性初始化为默认值。 - 然后将
name和age赋值给相应的属性。
- 使用
-
-
接受姓名、年龄和地址的构造函数:
-
public Person(string name, int age, string address) : this(name, age):- 使用
: this(name, age)调用接受姓名和年龄的构造函数,先完成姓名和年龄的赋值。 - 最后将
address赋值给Address属性。
- 使用
-
-
-
-
Main方法中的使用:-
创建
Person类的三个对象:Person person1 = new Person();:使用无参构造函数创建对象,其属性为默认值。Person person2 = new Person("Alice", 30);:使用接受姓名和年龄的构造函数创建对象。Person person3 = new Person("Bob", 25, "123 Main St");:使用接受姓名、年龄和地址的构造函数创建对象。
-
好处:
- 代码复用:通过
this调用其他构造函数,可以避免代码重复,提高代码的可维护性。 - 简洁性:使构造函数的实现更简洁,逻辑更清晰,避免在多个构造函数中重复初始化代码。
注意事项:
-
构造函数的调用链必须以一个无参构造函数或直接赋值的方式结束,以确保最终完成对象的完整初始化。
-
构造函数的调用顺序是从最具体的构造函数开始,逐步调用更通用的构造函数,以完成对象的初始化。
这个示例展示了如何使用 this 关键字多次重载构造函数,通过调用链确保对象的属性得到正确的初始化。