1 核心设计
using System;
using System.Collections.Concurrent;
using System.Timers;
namespace PriorityTimerManager;
public sealed class PriorityTimerManager : IDisposable
{
public sealed class OperationContext
{
private OperationContext(string tag, int periodMs)
{
Tag = tag;
PeriodMs = periodMs;
}
public string Tag { get; } = "";
public int PeriodMs { get; }
public static OperationContext Create(string tag, int periodMs)
{
if (string.IsNullOrEmpty(tag))
{
throw new ArgumentException("Tag cannot be null or empty.", nameof(tag));
}
if (periodMs <= 0)
{
throw new ArgumentOutOfRangeException(nameof(periodMs), "Period must be greater than zero.");
}
return new OperationContext(tag, periodMs);
}
}
private sealed class Candidate
{
private Candidate(int periodMs, int basePriority, int strength, DateTime now, string tag, Action action)
{
PeriodMs = periodMs;
BasePriority = basePriority;
Strength = strength;
Arrival = now;
SourceTag = tag;
Execute = action;
}
public int PeriodMs { get; }
public int BasePriority { get; } // 数字越小优先级越高
public int Strength { get; } // 数字越小优先级越高
public DateTime Arrival { get; }
public string SourceTag { get; } = "";
public Action Execute { get; } = () => { };
public static Candidate Create(int periodMs, int basePriority, int strength, DateTime now, string tag, Action action)
{
if (string.IsNullOrEmpty(tag))
{
throw new ArgumentException("Tag cannot be null or empty.", nameof(tag));
}
if (periodMs <= 0)
{
throw new ArgumentOutOfRangeException(nameof(periodMs), "Period must be greater than zero.");
}
if (basePriority < 0)
{
throw new ArgumentOutOfRangeException(nameof(basePriority), "Base priority must be non-negative.");
}
if (strength < 0)
{
throw new ArgumentOutOfRangeException(nameof(strength), "Strength must be non-negative.");
}
return new Candidate(periodMs, basePriority, strength, now, tag, action);
}
}
private sealed class CycleState
{
public readonly object LockObj = new();
public Candidate? CurrentWinner;
public Timer ResetTimer;
private CycleState(Timer resetTimer)
{
ResetTimer = resetTimer;
}
public static CycleState Create(Timer resetTimer)
{
if (resetTimer == null)
{
throw new ArgumentNullException(nameof(resetTimer), "Timer cannot be null.");
}
return new CycleState(resetTimer);
}
}
private readonly ConcurrentDictionary<int, CycleState> _cycleStates = new();
/// <summary>
/// 决定操作强度的策略函数(可外部替换)
/// </summary>
public Func<OperationContext, int> StrengthStrategy { get; set; } = ctx => 100; // 默认所有操作强度=100(最弱)
/// <summary>
/// 注册一个优先级定时器
/// </summary>
/// <param name="periodMs"></param>
/// <param name="basePriority"></param>
/// <param name="tag"></param>
/// <param name="action"></param>
/// <returns></returns>
public Timer RegisterTimer(int periodMs, int basePriority, string tag, Action action)
{
// 确保该周期有对应状态
var cs = _cycleStates.GetOrAdd(periodMs, p =>
{
var resetTimer = new Timer(p);
var state = CycleState.Create(resetTimer);
resetTimer.AutoReset = true;
resetTimer.Elapsed += (s, e) => ResetCycle(state, p);
resetTimer.Start();
return state;
});
// 用户任务定时器
var t = new Timer(periodMs)
{
AutoReset = true
};
t.Elapsed += (s, e) =>
{
var strength = StrengthStrategy(OperationContext.Create(tag, periodMs));
var c = Candidate.Create(periodMs, basePriority, strength, DateTime.Now, tag, action);
OnTimerFired(cs, c);
};
t.Start();
return t;
}
private void OnTimerFired(CycleState cs, Candidate c)
{
lock (cs.LockObj)
{
if (cs.CurrentWinner == null)
{
cs.CurrentWinner = c;
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 初次执行: {c.SourceTag}");
c.Execute();
return;
}
var cur = cs.CurrentWinner;
bool shouldPreempt = (c.Strength < cur.Strength) ||
(c.Strength == cur.Strength && c.BasePriority < cur.BasePriority) ||
(c.Strength == cur.Strength && c.BasePriority == cur.BasePriority &&
c.Arrival < cur.Arrival);
if (shouldPreempt)
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 抢占: {c.SourceTag} 覆盖 {cur.SourceTag}");
cs.CurrentWinner = c;
c.Execute();
}
else
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] 忽略: {c.SourceTag} (被 {cur.SourceTag} 压制)");
}
}
}
private static void ResetCycle(CycleState state, int periodMs)
{
lock (state.LockObj)
{
state.CurrentWinner = null;
Console.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] ===== 新周期开始 ({periodMs}ms) =====");
}
}
public void Dispose()
{
foreach (var kv in _cycleStates)
{
kv.Value.ResetTimer.Dispose();
}
}
}
2 使用
using System;
namespace PriorityTimerManager;
class Program
{
static void Main()
{
using var manager = new PriorityTimerManager();
// 配置强度策略
manager.StrengthStrategy = ctx =>
{
return ctx.Tag switch
{
"写0" => 0, // 最强
"写1" => 5,
"写2" => 10,
"写X" => 50,
_ => 100 // 默认最弱
};
};
// 6 秒周期任务
manager.RegisterTimer(6000, basePriority: 2, tag: "写1", () => Console.WriteLine("执行: 写 1"));
manager.RegisterTimer(6000, basePriority: 1, tag: "写0", () => Console.WriteLine("执行: 写 0"));
manager.RegisterTimer(6000, basePriority: 3, tag: "写2", () => Console.WriteLine("执行: 写 2"));
// 10 秒周期任务
manager.RegisterTimer(10000, basePriority: 2, tag: "写X", () => Console.WriteLine("执行: 写 X"));
manager.RegisterTimer(10000, basePriority: 1, tag: "写0_10s", () => Console.WriteLine("执行: 写 0 (10s)"));
Console.WriteLine("定时器运行中,按任意键退出...");
Console.ReadKey();
}
}