using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Reflection;
using System.Text;
using System.Threading;
namespace Apewer
{
/// Cron 特性,默认间隔为 60 秒。
[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;
/// 获取秒数。
public int Seconds { get { return _seconds; } }
/// 创建 Cron 特性,可指定两次 Cron 执行的间隔秒数。
public CronAttribute(int seconds = 60)
{
_mode = 1;
_seconds = seconds;
if (seconds < 0) throw new ArgumentOutOfRangeException(nameof(seconds));
}
/// 创建 Cron 特性,在每个周期中延迟指定秒数后执行。
///
public CronAttribute(CronCycle cycle, int seconds = 0)
{
_mode = 2;
_cycle = cycle;
_seconds = seconds;
if (seconds < 0) throw new ArgumentOutOfRangeException(nameof(seconds));
}
/// 生成 Json 对象。
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;
}
///
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;
Logger _logger = null;
bool _event = false;
// 实时。
Thread _thread = null;
bool _alive = false;
bool _break = false;
/// 运行当前 Cron。
void Run()
{
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();
});
}
// 当前进程睡眠。
static void Sleep() => Thread.Sleep(500);
// 线程负载。
void Payload()
{
// 基于间隔时间调用。
if (_mode == 1)
{
// 初始值为最小值,保证第一次启动时必定运行。
var next = DateTime.MinValue;
while (true)
{
if (_break || _break_all) break;
if (_now < next)
{
Sleep();
if (_break || _break_all) 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_all) break;
Sleep();
if (_break || _break_all) 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_all) break;
if (_now < next)
{
Sleep();
if (_break || _break_all) 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;
}
}
}
UsePool(pool =>
{
if (pool.ContainsKey(_type)) pool.Remove(_type);
_break = false;
_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 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 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;
/// 获取状态,指示打断 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)
{
var ab = new ArrayBuilder();
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;
}
/// 启动指定的 Cron。
///
public static void Start(CronAttribute cron)
{
if (cron == null) throw new ArgumentNullException(nameof(cron));
cron.Run();
}
/// 开始 Cron 调用(阻塞当前线程)。
/// 包含 Cron 的程序集,不指定此参数时将在 AppDomain 中搜索。
/// 日志记录程序,不指定此参数时将使用 Logger.Default。
/// 对 Cron 开始和结束记录日志。
///
/// 参数
/// - assemblies: 包含 Cron 的程序集,不指定此参数时将在 AppDomain 中搜索;
/// - logger: 日志记录程序,不指定此参数时将使用 Logger.Default;
/// - logEvent:对 Cron 开始和结束记录日志。
///
public static void Start(IEnumerable assemblies = null, Logger logger = null, bool logEvent = true)
{
if (logger == null) logger = Logger.Default;
// 设置控制台事件。
Console.CancelKeyPress += (s, e) =>
{
_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。");
// 启动 Cron。
foreach (var cron in crons) Start(cron);
// 等待 Cron 退出。
while (true)
{
Thread.Sleep(100);
_now = DateTime.Now;
if (AliveCount < 1) break;
}
}
logger.Text(nameof(CronAttribute), "Crons ended。");
}
/// 打断指定的 Cron。
///
public static void Abort(Type type)
{
if (type == null) throw new ArgumentNullException(nameof(type));
UsePool(pool =>
{
if (pool.TryGetValue(type, out var cron))
{
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。
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_all = true;
}
/// 打断 Cron 循环并等待 Job 执行结束。等待指定的时间后打断正在执行的 Job。
/// 强制打断前的等待毫秒数,指定为负数时将无限等待。
public static void Break(int timeout)
{
_break_all = true;
const int interval = 100;
if (timeout < 0)
{
while (AliveCount > 0) Thread.Sleep(interval);
return;
}
else
{
if (timeout > 0)
{
var remains = timeout;
while (remains > 0 && AliveCount > 0)
{
Thread.Sleep(interval);
remains -= interval;
}
}
Abort();
}
}
#endregion
}
}