From e5712f5b3f22f14314a10656df1d1ccaafd7c13c Mon Sep 17 00:00:00 2001 From: Elivo Date: Wed, 10 Nov 2021 02:51:21 +0800 Subject: [PATCH] Apewer-6.5.2 --- Apewer.Source/Source/Access.cs | 2 +- Apewer.Source/Source/MySql.cs | 2 +- Apewer.Source/Source/SqlClient.cs | 2 +- Apewer.Source/Source/Sqlite.cs | 2 +- Apewer/CronAttribute.cs | 144 +++++++++++++++++------ Apewer/CronInstance.cs | 95 --------------- Apewer/CronInvoker.cs | 189 ------------------------------ Apewer/RuntimeUtility.cs | 12 +- Apewer/Source/DbClient.cs | 2 +- Apewer/_Common.props | 2 +- ChangeLog.md | 3 + 11 files changed, 124 insertions(+), 331 deletions(-) delete mode 100644 Apewer/CronInstance.cs delete mode 100644 Apewer/CronInvoker.cs diff --git a/Apewer.Source/Source/Access.cs b/Apewer.Source/Source/Access.cs index f8b34e5..42f0bc3 100644 --- a/Apewer.Source/Source/Access.cs +++ b/Apewer.Source/Source/Access.cs @@ -214,7 +214,7 @@ namespace Apewer.Source { using (var da = new OleDbDataAdapter(sql, _connection)) { - const string name = "result"; + var name = "table_" + Guid.NewGuid().ToString("\n"); da.Fill(ds, name); var table = ds.Tables[name]; return new Query(table); diff --git a/Apewer.Source/Source/MySql.cs b/Apewer.Source/Source/MySql.cs index 11d1c90..a24c8b7 100644 --- a/Apewer.Source/Source/MySql.cs +++ b/Apewer.Source/Source/MySql.cs @@ -223,7 +223,7 @@ namespace Apewer.Source { using (var da = new MySqlDataAdapter(sql, _connection)) { - const string name = "result"; + var name = "table_" + Guid.NewGuid().ToString("\n"); da.Fill(ds, name); var table = ds.Tables[name]; return new Query(table); diff --git a/Apewer.Source/Source/SqlClient.cs b/Apewer.Source/Source/SqlClient.cs index 08e1513..1679f6f 100644 --- a/Apewer.Source/Source/SqlClient.cs +++ b/Apewer.Source/Source/SqlClient.cs @@ -235,7 +235,7 @@ namespace Apewer.Source { using (var da = new SqlDataAdapter(sql, _db)) { - const string name = "resule"; + var name = "table_" + Guid.NewGuid().ToString("\n"); da.Fill(ds, name); var table = ds.Tables[name]; return new Query(table, true); diff --git a/Apewer.Source/Source/Sqlite.cs b/Apewer.Source/Source/Sqlite.cs index bc3e89e..409c039 100644 --- a/Apewer.Source/Source/Sqlite.cs +++ b/Apewer.Source/Source/Sqlite.cs @@ -212,7 +212,7 @@ namespace Apewer.Source { using (var da = new SQLiteDataAdapter(sql, _db)) { - const string name = "result"; + var name = "table_" + Guid.NewGuid().ToString("\n"); da.Fill(dataset, name); var table = dataset.Tables[name]; return new Query(table); diff --git a/Apewer/CronAttribute.cs b/Apewer/CronAttribute.cs index 73a00e1..8d3ea40 100644 --- a/Apewer/CronAttribute.cs +++ b/Apewer/CronAttribute.cs @@ -1,8 +1,8 @@ -using Apewer.Web; -using System; +using System; using System.Collections.Generic; using System.Reflection; using System.Text; +using System.Threading; namespace Apewer { @@ -17,63 +17,135 @@ namespace Apewer private int _interval; /// 两次 Cron 执行的间隔毫秒数。 - public int Interval + public int Interval { get { return _interval; } } + + /// 创建 Cron 特性,可指定两次 Cron 执行的间隔毫秒数。 + public CronAttribute(int interval = DefaultInterval) { _interval = interval; } + + #region Payload + + // 传入。 + Type _type = null; + Logger _logger = null; + + // 实时。 + Thread _thread = null; + bool _alive = false; + + void Run() { - get { return _interval; } + _alive = true; + _thread = new Thread(Payload); + _thread.IsBackground = false; + _thread.Start(); } - /// 创建 Cron 特性,可指定两次 Cron 执行的间隔毫秒数。 - public CronAttribute(int interval = DefaultInterval) + void Payload() { - _interval = interval; + 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 - private static Class _invoker = new Class(); + static object _start = new object(); + static CronAttribute[] _crons = null; + static bool _break = false; - /// 开始 Cron 调用(不阻塞当前线程)。 - /// - /// 参数
- /// - assemblies: 包含 Cron 的程序集,不指定此参数时将在 AppDomain 中搜索;
- /// - logger: 日志记录程序,不指定此参数时将使用 Logger.Default。
- ///
- public static void Start(IEnumerable assemblies = null, Logger logger = null) + static CronAttribute[] Init(IEnumerable assemblies = null, Logger logger = null) { - CronInvoker instance = null; - lock (_invoker) + var attributes = new List(); + var added = new List(); + if (assemblies == null) assemblies = AppDomain.CurrentDomain.GetAssemblies(); + foreach (var assembly in assemblies) { - if (_invoker) return; - instance = new CronInvoker(); - _invoker.Value = instance; + var types = assembly.GetTypes(); + foreach (var type in types) + { + var attribute = RuntimeUtility.GetAttribute(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); + } } - instance.Logger = logger ?? Logger.Default; - instance.Load(assemblies ?? AppDomain.CurrentDomain.GetAssemblies()); - - Console.CancelKeyPress += (s, e) => - { - Break(); - e.Cancel = true; - }; - instance.Start(); + return attributes.ToArray(); } - /// 在当前线程开始 Cron 调用(阻塞当前线程)。 + /// 开始 Cron 调用(阻塞当前线程)。 /// /// 参数
/// - assemblies: 包含 Cron 的程序集,不指定此参数时将在 AppDomain 中搜索;
/// - logger: 日志记录程序,不指定此参数时将使用 Logger.Default。
///
- public static void Start(Logger logger, IEnumerable assemblies = null) => Start(assemblies, logger); + public static void Start(IEnumerable 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 已结束。"); + } + } /// 打断 Cron 循环,不打断正在执行的 Cron。 public static void Break() { - lock (_invoker) - { - if (!_invoker) return; - _invoker.Value.Break(); - } + _break = true; } #endregion diff --git a/Apewer/CronInstance.cs b/Apewer/CronInstance.cs deleted file mode 100644 index c9f8829..0000000 --- a/Apewer/CronInstance.cs +++ /dev/null @@ -1,95 +0,0 @@ -using Apewer; -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading; - -namespace Apewer.Web -{ - - internal sealed class CronInstance - { - - internal bool _latest = false; - internal Type _type = null; - internal Logger _logger = null; - internal Class _ended = null; - internal CronAttribute _attribute = null; - internal CronInvoker _invoker = null; - - private Thread _thread = null; - private bool _break = false; - - #region properties - - // 当前线程正在运行。 - public bool Alive { get => GetAlive(); } - - // 再次启动 Cron 的时间间隔。 - public int Interval { get => GetInterval(); } - - // 最后一次检查的 Alive 值。 - public bool Latest { get => _latest; } - - // Cron 类型。 - public Type Type { get => _type; } - - public CronAttribute Attribute { get => _attribute; } - - public Class Ended { get => _ended; } - - #endregion - - public CronInstance() - { - _thread = new Thread(Listen); - _thread.IsBackground = true; - } - - /// 打断循环。 - public void Break() => _break = true; - - /// 启动线程执行任务。 - public void Start() - { - if (Alive) return; - _thread = new Thread(Listen); - _thread.IsBackground = true; - _thread.Start(); - } - - int GetInterval() - { - if (_attribute == null) _attribute = new CronAttribute(); - return _attribute.Interval; - } - - bool GetAlive() - { - if (_thread == null) return false; - if (_thread.IsAlive != true) return false; - if (_thread.ThreadState != ThreadState.Running) return false; - return true; - } - - void Listen() - { - if (Type == null) return; - var instance = null as object; - try - { - instance = Activator.CreateInstance(Type); - } - catch (Exception exception) - { - Log(Type.FullName, exception.GetType().FullName, exception.Message); - } - RuntimeUtility.Dispose(instance); - _thread = null; - } - - void Log(params object[] content) => _logger.Text(Type.FullName, content); - - } - -} diff --git a/Apewer/CronInvoker.cs b/Apewer/CronInvoker.cs deleted file mode 100644 index 0a45106..0000000 --- a/Apewer/CronInvoker.cs +++ /dev/null @@ -1,189 +0,0 @@ -using Apewer; -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Text; -using System.Threading; - -namespace Apewer.Web -{ - - /// Cron 调度器。 - internal sealed class CronInvoker - { - - #region Instance - - private List _assemblies = null; - private List _instances = null; - private bool _break = false; - private Logger _logger = null; - - /// 获取或设置日志记录器。 - public Logger Logger { get { return _logger; } set { _logger = value; } } - - private void Log(object content) => _logger?.Text("Cron", content); - - /// 加载程序集。 - public void Load(IEnumerable assemblies) - { - Log("开始加载程序集。"); - if (_assemblies == null) _assemblies = new List(); - if (_assemblies.Count > 0) - { - Log("程序集列表为空。"); - return; - } - - if (assemblies == null) - { - _assemblies.AddRange(AppDomain.CurrentDomain.GetAssemblies()); - } - else - { - foreach (var assembly in assemblies) - { - if (assembly == null) continue; - if (_assemblies.Contains(assembly)) continue; - _assemblies.Add(assembly); - } - } - - var count = _assemblies.Count.ToString(); - Log($"已加载 {count} 个程序集。"); - } - - /// 通知打断循环,所有 Cron 执行结束后退出。 - public void Break() => _break = true; - - /// 开始 Cron 调用。 - public void Start() - { - if (_instances != null) return; - _instances = GetInstances(); - - var count = _assemblies.Count.ToString(); - Log($"检查到 {count} 个 Cron 类型。"); - - foreach (var i in _instances) Log(i.Type.FullName); - while (true) - { - // CronLog.Write("Tick"); - var alive = 0; - foreach (var i in _instances) - { - // 跳出。 - if (i.Alive) alive++; - if (_break) - { - i.Break(); - break; - } - - // 当前线程正在活动。 - if (i.Alive) - { - i._latest = true; - continue; - } - - // 记录 Cron 结束时间,根据结束时间判断再次启动 Cron。 - if (i.Latest) - { - Log($"{i.Type.FullName} Ended"); - i._ended = new Class(DateTime.Now); - i._latest = false; - } - - if (i.Ended == null) - { - Log($"{i.Type.FullName} Beginning"); - i.Start(); - i._latest = true; - } - else - { - var span = DateTime.Now - i.Ended.Value; - if (span.TotalMilliseconds >= Convert.ToDouble(i.Interval)) - { - Log($"{i.Type.FullName} Beginning"); - i.Start(); - i._latest = true; - } - } - } - - if (_break && alive < 1) - { - break; - } - - Thread.Sleep(500); - GC.Collect(); - } - - Log("循环结束,即将退出。"); - } - - private List GetInstances() - { - var list = new List(); - - var types = GetTypes(); - foreach (var type in types) - { - var attribute = RuntimeUtility.GetAttribute(type, false); - if (attribute == null) continue; - - var instance = new CronInstance(); - instance._invoker = this; - instance._attribute = attribute; - instance._type = type; - instance._logger = Logger; - - list.Add(instance); - } - - return list; - } - - private List GetTypes() - { - var list = new List(); - - var assemblies = _assemblies; - foreach (var assembly in assemblies) - { - var types = RuntimeUtility.GetTypes(assembly); - foreach (var type in types) - { - if (!type.IsPublic) continue; - if (type.IsAbstract) continue; - if (!RuntimeUtility.CanNew(type)) continue; - - list.Add(type); - } - } - - return list; - } - - #endregion - - #region Static - - // 在当前线程开始 Cron 调用 。 - public static CronInvoker Start(IEnumerable assemblies = null, Logger logger = null) - { - var instance = new CronInvoker(); - instance.Logger = logger; - instance.Load(assemblies ?? AppDomain.CurrentDomain.GetAssemblies()); - instance.Start(); - return instance; - } - - #endregion - - } - -} diff --git a/Apewer/RuntimeUtility.cs b/Apewer/RuntimeUtility.cs index fc63171..6b2a69a 100644 --- a/Apewer/RuntimeUtility.cs +++ b/Apewer/RuntimeUtility.cs @@ -608,19 +608,21 @@ namespace Apewer /// 当前应用程序由 IIS 托管。 public static bool InIIS() { -#if NETFRAMEWORK if (_InIIS != null) return _InIIS.Value; - var assemblies = AppDomain.CurrentDomain.GetAssemblies(); - foreach (var assembly in assemblies) + + var dll = Path.GetFileName(Assembly.GetExecutingAssembly().Location); + var path1 = StorageUtility.CombinePath(ApplicationPath, dll); + if (!File.Exists(path1)) { - if (assembly.FullName.StartsWith("System.Web,")) + var path2 = StorageUtility.CombinePath(ApplicationPath, "bin", dll); + if (File.Exists(path2)) { _InIIS = new Class(true); return true; } } + _InIIS = new Class(false); -#endif return false; } diff --git a/Apewer/Source/DbClient.cs b/Apewer/Source/DbClient.cs index 375dd76..948e223 100644 --- a/Apewer/Source/DbClient.cs +++ b/Apewer/Source/DbClient.cs @@ -181,7 +181,7 @@ namespace Apewer.Source { using (var da = NewDataAdapter(sql)) { - const string name = "result"; + var name = "table_" + Guid.NewGuid().ToString("\n"); da.Fill(ds, name); var table = ds.Tables[name]; return new Query(table, true); diff --git a/Apewer/_Common.props b/Apewer/_Common.props index 6251927..e65c532 100644 --- a/Apewer/_Common.props +++ b/Apewer/_Common.props @@ -14,7 +14,7 @@ Apewer Libraries - 6.5.1 + 6.5.2 diff --git a/ChangeLog.md b/ChangeLog.md index 3df978c..2864976 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,6 +1,9 @@  ### 最新提交 +### 6.5.2 +- Cron:修正多线程下同一类型并发的问题。 + ### 6.5.1 - Source:修正 Fill DateTime 报错的问题。