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 报错的问题。