You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

155 lines
4.9 KiB

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Threading;
namespace Apewer
{
/// <summary>Cron 特性,默认间隔为 60000 毫秒。</summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public sealed class CronAttribute : Attribute
{
internal const int DefaultInterval = 60000;
private int _interval;
/// <summary>两次 Cron 执行的间隔毫秒数。</summary>
public int Interval { get { return _interval; } }
/// <summary>创建 Cron 特性,可指定两次 Cron 执行的间隔毫秒数。</summary>
public CronAttribute(int interval = DefaultInterval) { _interval = interval; }
#region Payload
// 传入。
Type _type = null;
Logger _logger = null;
// 实时。
Thread _thread = null;
bool _alive = false;
void Run()
{
_alive = true;
_thread = new Thread(Payload);
_thread.IsBackground = false;
_thread.Start();
}
void Payload()
{
var moment = 500;
var delay = 0;
var sender = $"Cron-{_type.Name}";
while (true)
{
if (_break) break;
if (delay <= 0)
{
delay = _interval;
_logger.Text(sender, "Beginning");
try
{
var instance = Activator.CreateInstance(_type);
RuntimeUtility.Dispose(instance);
_logger.Text(sender, "Ended");
}
catch (Exception ex)
{
_logger.Exception(sender, ex);
}
}
else
{
Thread.Sleep(moment);
delay = delay - moment;
}
}
_alive = false;
}
#endregion
#region CronInvoker
static object _start = new object();
static CronAttribute[] _crons = null;
static bool _break = false;
static CronAttribute[] Init(IEnumerable<Assembly> assemblies = null, Logger logger = null)
{
var attributes = new List<CronAttribute>();
var added = new List<string>();
if (assemblies == null) assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in assemblies)
{
var types = assembly.GetTypes();
foreach (var type in types)
{
var attribute = RuntimeUtility.GetAttribute<CronAttribute>(type);
if (attribute == null) continue;
if (added.Contains(type.FullName)) continue;
if (!RuntimeUtility.CanNew(type)) continue;
attribute._type = type;
attribute._logger = logger;
attributes.Add(attribute);
}
}
return attributes.ToArray();
}
/// <summary>开始 Cron 调用(阻塞当前线程)。</summary>
/// <remarks>
/// 参数<br />
/// - assemblies: 包含 Cron 的程序集,不指定此参数时将在 AppDomain 中搜索;<br />
/// - logger: 日志记录程序,不指定此参数时将使用 Logger.Default。<br />
/// </remarks>
public static void Start(IEnumerable<Assembly> assemblies = null, Logger logger = null)
{
if (logger == null) logger = Logger.Default;
lock (_start)
{
// 初始化。
_crons = Init(assemblies, logger);
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);
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;
}
#endregion
}
}