可抢占执行模型的同周期多定时器管理器设计

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();
    }
}
posted @ 2025-08-20 00:41  Ar4te  阅读(18)  评论(0)    收藏  举报