Browse Source

Apewer-6.5.2

dev
王厅 4 years ago
parent
commit
e5712f5b3f
  1. 2
      Apewer.Source/Source/Access.cs
  2. 2
      Apewer.Source/Source/MySql.cs
  3. 2
      Apewer.Source/Source/SqlClient.cs
  4. 2
      Apewer.Source/Source/Sqlite.cs
  5. 144
      Apewer/CronAttribute.cs
  6. 95
      Apewer/CronInstance.cs
  7. 189
      Apewer/CronInvoker.cs
  8. 12
      Apewer/RuntimeUtility.cs
  9. 2
      Apewer/Source/DbClient.cs
  10. 2
      Apewer/_Common.props
  11. 3
      ChangeLog.md

2
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);

2
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);

2
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);

2
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);

144
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;
/// <summary>两次 Cron 执行的间隔毫秒数。</summary>
public int Interval
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()
{
get { return _interval; }
_alive = true;
_thread = new Thread(Payload);
_thread.IsBackground = false;
_thread.Start();
}
/// <summary>创建 Cron 特性,可指定两次 Cron 执行的间隔毫秒数。</summary>
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<CronInvoker> _invoker = new Class<CronInvoker>();
static object _start = new object();
static CronAttribute[] _crons = null;
static bool _break = false;
/// <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)
static CronAttribute[] Init(IEnumerable<Assembly> assemblies = null, Logger logger = null)
{
CronInvoker instance = null;
lock (_invoker)
var attributes = new List<CronAttribute>();
var added = new List<string>();
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<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);
}
}
instance.Logger = logger ?? Logger.Default;
instance.Load(assemblies ?? AppDomain.CurrentDomain.GetAssemblies());
Console.CancelKeyPress += (s, e) =>
{
Break();
e.Cancel = true;
};
instance.Start();
return attributes.ToArray();
}
/// <summary>在当前线程开始 Cron 调用(阻塞当前线程)。</summary>
/// <summary>开始 Cron 调用(阻塞当前线程)。</summary>
/// <remarks>
/// 参数<br />
/// - assemblies: 包含 Cron 的程序集,不指定此参数时将在 AppDomain 中搜索;<br />
/// - logger: 日志记录程序,不指定此参数时将使用 Logger.Default。<br />
/// </remarks>
public static void Start(Logger logger, IEnumerable<Assembly> assemblies = null) => Start(assemblies, logger);
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()
{
lock (_invoker)
{
if (!_invoker) return;
_invoker.Value.Break();
}
_break = true;
}
#endregion

95
Apewer/CronInstance.cs

@ -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<DateTime> _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<DateTime> Ended { get => _ended; }
#endregion
public CronInstance()
{
_thread = new Thread(Listen);
_thread.IsBackground = true;
}
/// <summary>打断循环。</summary>
public void Break() => _break = true;
/// <summary>启动线程执行任务。</summary>
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);
}
}

189
Apewer/CronInvoker.cs

@ -1,189 +0,0 @@
using Apewer;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Threading;
namespace Apewer.Web
{
/// <summary>Cron 调度器。</summary>
internal sealed class CronInvoker
{
#region Instance
private List<Assembly> _assemblies = null;
private List<CronInstance> _instances = null;
private bool _break = false;
private Logger _logger = null;
/// <summary>获取或设置日志记录器。</summary>
public Logger Logger { get { return _logger; } set { _logger = value; } }
private void Log(object content) => _logger?.Text("Cron", content);
/// <summary>加载程序集。</summary>
public void Load(IEnumerable<Assembly> assemblies)
{
Log("开始加载程序集。");
if (_assemblies == null) _assemblies = new List<Assembly>();
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} 个程序集。");
}
/// <summary>通知打断循环,所有 Cron 执行结束后退出。</summary>
public void Break() => _break = true;
/// <summary>开始 Cron 调用。</summary>
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>(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<CronInstance> GetInstances()
{
var list = new List<CronInstance>();
var types = GetTypes();
foreach (var type in types)
{
var attribute = RuntimeUtility.GetAttribute<CronAttribute>(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<Type> GetTypes()
{
var list = new List<Type>();
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<Assembly> assemblies = null, Logger logger = null)
{
var instance = new CronInvoker();
instance.Logger = logger;
instance.Load(assemblies ?? AppDomain.CurrentDomain.GetAssemblies());
instance.Start();
return instance;
}
#endregion
}
}

12
Apewer/RuntimeUtility.cs

@ -608,19 +608,21 @@ namespace Apewer
/// <summary>当前应用程序由 IIS 托管。</summary>
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<bool>(true);
return true;
}
}
_InIIS = new Class<bool>(false);
#endif
return false;
}

2
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);

2
Apewer/_Common.props

@ -14,7 +14,7 @@
<!-- 程序集信息 -->
<PropertyGroup>
<Product>Apewer Libraries</Product>
<Version>6.5.1</Version>
<Version>6.5.2</Version>
</PropertyGroup>
<!-- NuGet -->

3
ChangeLog.md

@ -1,6 +1,9 @@

### 最新提交
### 6.5.2
- Cron:修正多线程下同一类型并发的问题。
### 6.5.1
- Source:修正 Fill DateTime 报错的问题。

Loading…
Cancel
Save