|
|
using System; using System.Collections.Generic; using System.Reflection; using System.Text; using System.Threading;
namespace Apewer {
/// <summary>Cron 特性,默认间隔为 60 秒。</summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] public sealed class CronAttribute : Attribute, IToJson {
private int _mode = 0; private int _seconds = 0; private CronCycle _cycle = CronCycle.Once;
/// <summary>获取秒数。</summary>
public int Seconds { get { return _seconds; } }
/// <summary>创建 Cron 特性,可指定两次 Cron 执行的间隔秒数。</summary>
public CronAttribute(int seconds = 60) { _mode = 1; _seconds = seconds; if (seconds < 0) throw new ArgumentOutOfRangeException(nameof(seconds)); }
/// <summary>创建 Cron 特性,在每个周期中延迟指定秒数后执行。</summary>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public CronAttribute(CronCycle cycle, int seconds = 0) { _mode = 2; _cycle = cycle; _seconds = seconds; if (seconds < 0) throw new ArgumentOutOfRangeException(nameof(seconds)); }
/// <summary>生成 Json 对象。</summary>
public Json ToJson() { var mode = _mode == 1 ? "interval" : "cycle"; var type = _type == null ? null : _type.FullName;
var json = Json.NewObject(); json.SetProperty("type", type); json.SetProperty("mode", mode); if (_mode == 2) json.SetProperty("cycle", _cycle.ToString().ToLower()); json.SetProperty("second", _seconds); return json; }
#region Payload
// 传入。
Type _type = null; Logger _logger = null; bool _event = false;
// 实时。
Thread _thread = null; bool _alive = false;
void Run() { _alive = true; _thread = new Thread(Payload); _thread.IsBackground = false; _thread.Start(); }
// 当前进程睡眠。
static void Sleep() => Thread.Sleep(500);
// 线程负载。
void Payload() { // 基于间隔时间调用。
if (_mode == 1) { // 初始值为最小值,保证第一次启动时必定运行。
var next = DateTime.MinValue; while (true) { if (_break) break; if (_now < next) { Sleep(); if (_break) break; continue; }
Invoke(); next = DateTime.Now.AddSeconds(_seconds); } }
// 仅执行一次,延迟启动。
if (_mode == 2 && _cycle == CronCycle.Once) { var next = DateTime.Now.AddSeconds(_seconds); while (_now < next) { if (_break) break; Sleep(); if (_break) break; } Invoke(); }
// 基于周期调用。
if (_mode == 2 && _cycle != CronCycle.Once) { // 计算当前期间的运行时间。
DateTime next = DateTime.Now; switch (_cycle) { case CronCycle.Minute: next = new DateTime(next.Year, next.Month, next.Day, next.Hour, next.Minute, 0, 0); break; case CronCycle.Hour: next = new DateTime(next.Year, next.Month, next.Day, next.Hour, 0, 0, 0); break; case CronCycle.Day: next = new DateTime(next.Year, next.Month, next.Day, 0, 0, 0, 0); break; case CronCycle.Week: next = new DateTime(next.Year, next.Month, next.Day, 0, 0, 0, 0).AddDays(-(int)next.DayOfWeek); break; } if (_seconds > 0) next = next.AddSeconds(_seconds);
// 启动循环。
while (true) { if (_break) break; if (_now < next) { Sleep(); if (_break) break; continue; }
Invoke(); switch (_cycle) { case CronCycle.Minute: next = next.AddMinutes(1); break; case CronCycle.Hour: next = next.AddHours(1); break; case CronCycle.Day: next = next.AddDays(1); break; case CronCycle.Week: next = next.AddDays(7); break; default: _alive = false; return; } } }
_alive = false; }
void Invoke() { var caption = CaptionAttribute.Parse(_type); var sender = caption?.Title; if (sender.IsEmpty()) sender = _type.Name; if (_event) _logger.Text(sender, "Beginning"); var instance = null as object; try { instance = Activator.CreateInstance(_type); if (_event) _logger.Text(sender, "Ended"); } catch (Exception ex) { _logger.Exception(ex.InnerException, sender); } RuntimeUtility.Dispose(instance); }
#endregion
#region CronInvoker
static object _start = new object(); static CronAttribute[] _crons = null; static bool _break = false; static DateTime _now;
static CronAttribute[] Init(IEnumerable<Assembly> assemblies, Logger logger, bool logEvent) { var ab = new ArrayBuilder<CronAttribute>(); if (assemblies == null) assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (var assembly in assemblies) { var types = assembly.GetTypes(); foreach (var type in types) { if (!type.IsClass) continue; if (type.IsAbstract) continue; if (!RuntimeUtility.CanNew(type)) continue;
var attributes = type.GetCustomAttributes(false); if (attributes == null) continue; foreach (var attribute in attributes) { var cron = attribute as CronAttribute; if (cron == null) continue; cron._type = type; cron._logger = logger; cron._event = logEvent; ab.Add(cron); } } } var array = ab.Export(); return array; }
/// <summary>开始 Cron 调用(阻塞当前线程)。</summary>
/// <param name="assemblies">包含 Cron 的程序集,不指定此参数时将在 AppDomain 中搜索。</param>
/// <param name="logger">日志记录程序,不指定此参数时将使用 Logger.Default。</param>
/// <param name="logEvent">对 Cron 开始和结束记录日志。</param>
/// <remarks>
/// 参数<br />
/// - assemblies: 包含 Cron 的程序集,不指定此参数时将在 AppDomain 中搜索;<br />
/// - logger: 日志记录程序,不指定此参数时将使用 Logger.Default;<br />
/// - logEvent:对 Cron 开始和结束记录日志。
/// </remarks>
public static void Start(IEnumerable<Assembly> assemblies = null, Logger logger = null, bool logEvent = true) { if (logger == null) logger = Logger.Default; lock (_start) { // 初始化。
_now = DateTime.Now; _crons = Init(assemblies, logger, logEvent); if (_crons.Length < 1) { logger.Error(nameof(CronAttribute), "没有找到带有 Cron 特性的类型。"); return; }
// 启动线程。
Console.CancelKeyPress += (s, e) => { _break = true; e.Cancel = true; }; logger.Text(nameof(CronAttribute), $"启动 {_crons.Length} 个 Cron 线程。"); foreach (var cron in _crons) cron.Run();
// 监视退出状态。
while (true) { Thread.Sleep(300); _now = DateTime.Now; var alive = 0; for (var i = 0; i < _crons.Length; i++) if (_crons[i]._alive) alive += 1; if (alive < 1) break; } logger.Text(nameof(CronAttribute), "所有 Cron 已结束。"); } }
/// <summary>打断 Cron 循环,不打断正在执行的 Cron。</summary>
public static void Break() { _break = true; }
/// <summary>获取状态,指示打断 Cron 循环。</summary>
public static bool Breaking { get => _break; }
#endregion
}
}
|