From a0b786b6b25acc9c299ef37edf668fe80fabaf30 Mon Sep 17 00:00:00 2001 From: Elivo Date: Tue, 29 Jul 2025 12:54:34 +0800 Subject: [PATCH] =?UTF-8?q?Cron=EF=BC=8C=E5=A2=9E=E5=8A=A0=20Break(Type)?= =?UTF-8?q?=20=E6=96=B9=E6=B3=95=EF=BC=8C=E5=85=81=E8=AE=B8=20Cron=20?= =?UTF-8?q?=E6=89=93=E6=96=AD=E8=87=AA=E5=B7=B1=E7=9A=84=E5=BE=AA=E7=8E=AF?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Apewer/CronAttribute.cs | 248 ++++++++++++++++++++++++++++++---------- 1 file changed, 190 insertions(+), 58 deletions(-) diff --git a/Apewer/CronAttribute.cs b/Apewer/CronAttribute.cs index fcd9581..0f5adcc 100644 --- a/Apewer/CronAttribute.cs +++ b/Apewer/CronAttribute.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.Design; using System.Reflection; using System.Text; using System.Threading; @@ -8,10 +9,12 @@ namespace Apewer { /// Cron 特性,默认间隔为 60 秒。 - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] public sealed class CronAttribute : Attribute, IToJson { + #region attribute + private int _mode = 0; private int _seconds = 0; private CronCycle _cycle = CronCycle.Once; @@ -51,7 +54,23 @@ namespace Apewer return json; } - #region Payload + /// + public override string ToString() + { + if (_type != null) + { + if (_mode == 1) return $"Type = {_type.FullName}, Seconds = {_seconds}"; + if (_mode == 2) return $"Type = {_type.FullName}, Cycle = {_cycle}, Seconds = {_seconds}"; + } + return base.ToString(); + } + + #endregion + + #region payload + + /// 执行 Cron 的类。 + public Type Type { get => _type; } // 传入。 Type _type = null; @@ -61,13 +80,25 @@ namespace Apewer // 实时。 Thread _thread = null; bool _alive = false; + bool _break = false; + /// 运行当前 Cron。 void Run() { - _alive = true; - _thread = new Thread(Payload); - _thread.IsBackground = false; - _thread.Start(); + if (_alive) return; + if (_type == null) return; + + _break = false; + UsePool(pool => + { + if (pool.ContainsKey(_type)) return; + pool.Add(_type, this); + + _alive = true; + _thread = new Thread(Payload); + _thread.IsBackground = false; + _thread.Start(); + }); } // 当前进程睡眠。 @@ -83,11 +114,11 @@ namespace Apewer var next = DateTime.MinValue; while (true) { - if (_break) break; + if (_break || _break_all) break; if (_now < next) { Sleep(); - if (_break) break; + if (_break || _break_all) break; continue; } @@ -102,9 +133,9 @@ namespace Apewer var next = DateTime.Now.AddSeconds(_seconds); while (_now < next) { - if (_break) break; + if (_break || _break_all) break; Sleep(); - if (_break) break; + if (_break || _break_all) break; } Invoke(); } @@ -134,11 +165,11 @@ namespace Apewer // 启动循环。 while (true) { - if (_break) break; + if (_break || _break_all) break; if (_now < next) { Sleep(); - if (_break) break; + if (_break || _break_all) break; continue; } @@ -164,7 +195,13 @@ namespace Apewer } } - _alive = false; + UsePool(pool => + { + if (pool.ContainsKey(_type)) pool.Remove(_type); + + _break = false; + _alive = false; + }); } void Invoke() @@ -188,13 +225,52 @@ namespace Apewer #endregion - #region CronInvoker + #region pool + + static Dictionary _pool = new Dictionary(); + + static void UsePool(Action> callback) + { + if (callback == null) throw new ArgumentNullException(nameof(callback)); + lock (_pool) + { + callback.Invoke(_pool); + } + } - static object _start = new object(); - static CronAttribute[] _crons = null; - static bool _break = false; + static T UsePool(Func, T> callback) + { + if (callback == null) throw new ArgumentNullException(nameof(callback)); + lock (_pool) + { + return callback.Invoke(_pool); + } + } + + #endregion + + #region invoker + + static bool _break_all = false; static DateTime _now; - static int _crons_alive = 0; + + /// 获取状态,指示打断 Cron 循环。 + public static bool Breaking { get => _break_all; } + + /// 正在运行的 Cron 数量。 + public static int AliveCount { get => CountAlive(UsePool(pool => pool.Values)); } + + /// 获取正在运行的 Cron 数量。 + /// + static int CountAlive(IEnumerable crons) + { + var alive = 0; + foreach (var cron in crons) + { + if (cron._alive) alive += 1; + } + return alive; + } static CronAttribute[] Init(IEnumerable assemblies, Logger logger, bool logEvent) { @@ -226,6 +302,14 @@ namespace Apewer return array; } + /// 启动指定的 Cron。 + /// + public static void Start(CronAttribute cron) + { + if (cron == null) throw new ArgumentNullException(nameof(cron)); + cron.Run(); + } + /// 开始 Cron 调用(阻塞当前线程)。 /// 包含 Cron 的程序集,不指定此参数时将在 AppDomain 中搜索。 /// 日志记录程序,不指定此参数时将使用 Logger.Default。 @@ -239,71 +323,122 @@ namespace Apewer public static void Start(IEnumerable assemblies = null, Logger logger = null, bool logEvent = true) { if (logger == null) logger = Logger.Default; - lock (_start) + + // 设置控制台事件。 + Console.CancelKeyPress += (s, e) => { - // 初始化。 - _now = DateTime.Now; - _crons = Init(assemblies, logger, logEvent); - if (_crons.Length < 1) - { - logger.Error(nameof(CronAttribute), "没有找到带有 Cron 特性的类型。"); - return; - } + _break_all = true; + e.Cancel = true; + }; + + // 初始化。 + _now = DateTime.Now; + var crons = Init(assemblies, logger, logEvent); + if (crons.Length > 0) + { + logger.Text(nameof(CronAttribute), "Crons started。"); - // 启动线程。 - _crons_alive = _crons.Length; - Console.CancelKeyPress += (s, e) => - { - _break = true; - e.Cancel = true; - }; - logger.Text(nameof(CronAttribute), $"启动 {_crons.Length} 个 Cron 线程。"); - foreach (var cron in _crons) cron.Run(); + // 启动 Cron。 + foreach (var cron in crons) Start(cron); - // 监视退出状态。 + // 等待 Cron 退出。 while (true) { Thread.Sleep(100); _now = DateTime.Now; - var alive = 0; - for (var i = 0; i < _crons.Length; i++) if (_crons[i]._alive) alive += 1; - if (alive < 1) break; - _crons_alive = alive; + if (AliveCount < 1) break; } - logger.Text(nameof(CronAttribute), "所有 Cron 已结束。"); + } + logger.Text(nameof(CronAttribute), "Crons ended。"); } - /// 打断正在执行的 Cron。 - public static void Abort() + /// 打断指定的 Cron。 + /// + public static void Abort(Type type) { - var crons = _crons; - if (crons != null) + if (type == null) throw new ArgumentNullException(nameof(type)); + + UsePool(pool => { - foreach (var cron in crons) + if (pool.TryGetValue(type, out var cron)) { - if (cron == null) continue; try { cron._thread.Abort(); } catch { } } - } + + pool.Remove(type); + }); + } + + /// 打断指定的 Cron。 + /// + public static void Abort(object instance) + { + if (instance == null) throw new ArgumentNullException(nameof(instance)); + + if (instance is Type type) Abort(type); + else Abort(instance.GetType()); } - /// 打断 Cron 循环,不打断正在执行的 Cron。 + /// 打断所有 Cron。 + public static void Abort() + { + UsePool(pool => + { + foreach (var item in pool) + { + try + { + item.Value._thread.Abort(); + item.Value._alive = false; + item.Value._break = false; + } + catch { } + } + }); + } + + /// 打断 Cron 循环,不打断正在执行的 Job。 + /// + public static void Break(Type type) + { + if (type == null) throw new ArgumentNullException(nameof(type)); + + UsePool(pool => + { + if (pool.TryGetValue(type, out var cron)) + { + cron._break = true; + } + }); + } + + /// 打断 Cron 循环,不打断正在执行的 Job。 + /// + public static void Break(object instance) + { + if (instance == null) throw new ArgumentNullException(nameof(instance)); + + if (instance is Type type) Break(type); + else Break(instance.GetType()); + } + + /// 打断 Cron 循环,不打断正在执行的 Job。 public static void Break() { - _break = true; + _break_all = true; } - /// 打断 Cron 循环并等待 Cron 执行结束。等待指定的时间后打断正在执行的 Cron。 + /// 打断 Cron 循环并等待 Job 执行结束。等待指定的时间后打断正在执行的 Job。 /// 强制打断前的等待毫秒数,指定为负数时将无限等待。 public static void Break(int timeout) { - _break = true; + _break_all = true; const int interval = 100; if (timeout < 0) { - while (_crons_alive > 0) Thread.Sleep(interval); + while (AliveCount > 0) Thread.Sleep(interval); return; } else @@ -311,7 +446,7 @@ namespace Apewer if (timeout > 0) { var remains = timeout; - while (remains > 0 && _crons_alive > 0) + while (remains > 0 && AliveCount > 0) { Thread.Sleep(interval); remains -= interval; @@ -321,9 +456,6 @@ namespace Apewer } } - /// 获取状态,指示打断 Cron 循环。 - public static bool Breaking { get => _break; } - #endregion }