分析TaskManager.Instance.Initialize(); 下面是一个实体抽象父类,ScheduleTask 继承此类。
namespace Nop.Core { /// <summary> /// Base class for entities /// </summary> public abstract partial class BaseEntity { /// <summary> /// Gets or sets the entity identifier /// </summary> public int Id { get; set; } public override bool Equals(object obj) { return Equals(obj as BaseEntity); } private static bool IsTransient(BaseEntity obj) {//default返回类型默认值,int是0,string是null等 return obj != null && Equals(obj.Id, default(int)); } private Type GetUnproxiedType() { return GetType(); } public virtual bool Equals(BaseEntity other) { if (other == null) return false; if (ReferenceEquals(this, other)) return true; //如果2个数都不是0并且相等 if (!IsTransient(this) && !IsTransient(other) && Equals(Id, other.Id)) {//如果两个类型相同或是继承关系 返回true.IsAssignableFrom:确定当前实例的类型是否可以从指定类型的实例分配 //http://javapub.iteye.com/blog/764668 isAssignableFrom是用来判断一个类Class1和另一个类Class2是否相同或是另一个类的超类或接口 var otherType = other.GetUnproxiedType(); var thisType = GetUnproxiedType(); return thisType.IsAssignableFrom(otherType) || otherType.IsAssignableFrom(thisType); } return false; } public override int GetHashCode() { //http://baike.baidu.com/link?url=qKWysQqQA09bgOa6JzQylv7PGB-jiPMfUjAFvOIFqxxK45Vjkft7J3zOcib5zJVxcN2ZH4NeOkWlyh-079Cgyq 百度百科哈希吗, //哈希算法不一样,解释:哈希码产生的依据:哈希码并不是完全唯一的,它是一种算法,让同一个类的对象按照自己不同的特征尽量的有不同的哈希码,
//但不表示不同的对象哈希码完全不同。也有相同的情况,看程序员如何写哈希码的算法。 例如:OBJECT类型,由于内存地址不一样,所以哈希码也不一样。
//String,根据字符串用特殊算法返回哈希码。 int 哈希码与数值相同。 if (Equals(Id, default(int))) return base.GetHashCode(); return Id.GetHashCode(); } //操作符重载 public static bool operator ==(BaseEntity x, BaseEntity y) { return Equals(x, y); } public static bool operator !=(BaseEntity x, BaseEntity y) { return !(x == y); } } }
下面是计划任务类,含有名称、运行时段Seconds 、type、是否可用等
namespace Nop.Core.Domain.Tasks { public class ScheduleTask : BaseEntity { /// <summary> /// Gets or sets the name 类名应该是计划任务 Schedule计划、安排 /// </summary> public string Name { get; set; } /// <summary> /// Gets or sets the run period (in seconds) /// </summary> public int Seconds { get; set; } /// <summary> /// Gets or sets the type of appropriate ITask class /// </summary> public string Type { get; set; } /// <summary> /// Gets or sets the value indicating whether a task is enabled /// </summary> public bool Enabled { get; set; } /// <summary> /// Gets or sets the value indicating whether a task should be stopped on some error /// </summary> public bool StopOnError { get; set; } public DateTime? LastStartUtc { get; set; } public DateTime? LastEndUtc { get; set; } public DateTime? LastSuccessUtc { get; set; } } }
计划任务服务类,增删改查 计划任务等操作。
namespace Nop.Services.Tasks { /// <summary> /// Task service interface /// </summary> public partial interface IScheduleTaskService { /// <summary> /// Deletes a task /// </summary> /// <param name="task">Task</param> void DeleteTask(ScheduleTask task); /// <summary> /// Gets a task /// </summary> /// <param name="taskId">Task identifier</param> /// <returns>Task</returns> ScheduleTask GetTaskById(int taskId); /// <summary> /// Gets a task by its type /// </summary> /// <param name="type">Task type</param> /// <returns>Task</returns> ScheduleTask GetTaskByType(string type); /// <summary> /// Gets all tasks /// </summary> /// <param name="showHidden">A value indicating whether to show hidden records</param> /// <returns>Tasks</returns> IList<ScheduleTask> GetAllTasks(bool showHidden = false); /// <summary> /// Inserts a task /// </summary> /// <param name="task">Task</param> void InsertTask(ScheduleTask task); /// <summary> /// Updates the task /// </summary> /// <param name="task">Task</param> void UpdateTask(ScheduleTask task); }
下面是TaskManager.Instance.Initialize(); 的代码
/// <summary> /// Initializes the task manager with the property values specified in the configuration file. /// </summary> public void Initialize() { this._taskThreads.Clear(); var taskService = EngineContext.Current.Resolve<IScheduleTaskService>(); var scheduleTasks = taskService .GetAllTasks() .OrderBy(x => x.Seconds) .ToList(); //group by threads with the same seconds foreach (var scheduleTaskGrouped in scheduleTasks.GroupBy(x => x.Seconds)) { //create a thread var taskThread = new TaskThread { Seconds = scheduleTaskGrouped.Key }; foreach (var scheduleTask in scheduleTaskGrouped) { var task = new Task(scheduleTask); taskThread.AddTask(task); } this._taskThreads.Add(taskThread); } //sometimes a task period could be set to several hours (or even days). //in this case a probability that it'll be run is quite small (an application could be restarted) //we should manually run the tasks which weren't run for a long time var notRunTasks = scheduleTasks .Where(x => x.Seconds >= _notRunTasksInterval) .Where(x => !x.LastStartUtc.HasValue || x.LastStartUtc.Value.AddSeconds(_notRunTasksInterval) < DateTime.UtcNow) .ToList(); //create a thread for the tasks which weren't run for a long time if (notRunTasks.Count > 0) { var taskThread = new TaskThread { RunOnlyOnce = true, Seconds = 60 * 5 //let's run such tasks in 5 minutes after application start }; foreach (var scheduleTask in notRunTasks) { var task = new Task(scheduleTask); taskThread.AddTask(task); } this._taskThreads.Add(taskThread); } }
第一句 this._taskThreads.Clear(); 是清楚所有taskThreads, 下面是taskThreads的声明。
private readonly List<TaskThread> _taskThreads = new List<TaskThread>();
TaskThread的集合,其中TaskThread的声明如下:
namespace Nop.Services.Tasks { /// <summary> /// Represents task thread /// </summary> public partial class TaskThread : IDisposable { private Timer _timer; private bool _disposed; private readonly Dictionary<string, Task> _tasks; internal TaskThread() { this._tasks = new Dictionary<string, Task>(); this.Seconds = 10 * 60; } private void Run() { if (Seconds <= 0) return; this.StartedUtc = DateTime.UtcNow; this.IsRunning = true; foreach (Task task in this._tasks.Values) { task.Execute(); } this.IsRunning = false; } private void TimerHandler(object state) { this._timer.Change(-1, -1); this.Run(); if (this.RunOnlyOnce) { this.Dispose(); } else { this._timer.Change(this.Interval, this.Interval); } } /// <summary> /// Disposes the instance /// </summary> public void Dispose() { if ((this._timer != null) && !this._disposed) { lock (this) { this._timer.Dispose(); this._timer = null; this._disposed = true; } } } /// <summary> /// Inits a timer /// </summary> public void InitTimer() { if (this._timer == null) { this._timer = new Timer(new TimerCallback(this.TimerHandler), null, this.Interval, this.Interval); } } /// <summary> /// Adds a task to the thread /// </summary> /// <param name="task">The task to be added</param> public void AddTask(Task task) { if (!this._tasks.ContainsKey(task.Name)) { this._tasks.Add(task.Name, task); } } /// <summary> /// Gets or sets the interval in seconds at which to run the tasks /// </summary> public int Seconds { get; set; } /// <summary> /// Get or sets a datetime when thread has been started /// </summary> public DateTime StartedUtc { get; private set; } /// <summary> /// Get or sets a value indicating whether thread is running /// </summary> public bool IsRunning { get; private set; } /// <summary> /// Get a list of tasks /// </summary> public IList<Task> Tasks { get { var list = new List<Task>(); foreach (var task in this._tasks.Values) { list.Add(task); } return new ReadOnlyCollection<Task>(list); } } /// <summary> /// Gets the interval at which to run the tasks /// </summary> public int Interval { get { return this.Seconds * 1000; } } /// <summary> /// Gets or sets a value indicating whether the thread whould be run only once (per appliction start) /// </summary> public bool RunOnlyOnce { get; set; } } }
清除所有任务线程,后通过依赖注入获取所有的 IScheduleTaskService 计划任务服务类,IScheduleTaskService 通过GetAllTasks方法获得所有任务,根据Seconds进行排序 返回scheduleTasks枚举。
对GetAllTasks根据Seconds进行分组(也就是以Seconds为KEY),并循环每一个分组,new TaskThread类 代码:(其他代码在上面可找到)
var taskThread = new TaskThread { Seconds = scheduleTaskGrouped.Key };
然后再循环组内的每一个计划任务, new 一个 TASK(构造参数:计划任务),最后将new 的任务都添加到taskThread(任务线程内)。然后将所有的任务线程添加到List中。
后面代码的意思是,对运行时段大于30分钟的项目,如果启动时间为空,或者上次启动时间已超过30分钟,在5分钟后立即执行一次,仅执行一次。以后就根据时间上面taskThread的循环进行执行。
global.asax.cs 的TaskManager.Instance.Start(); 应该是开始任务运行,其代码如下:
/// <summary> /// Starts the task manager /// </summary> public void Start() { foreach (var taskThread in this._taskThreads) { taskThread.InitTimer(); } }
循环所有的任务线程,调用InitTimer方法。代码如下:
/// <summary> /// Inits a timer /// </summary> public void InitTimer() { if (this._timer == null) { this._timer = new Timer(new TimerCallback(this.TimerHandler), null, this.Interval, this.Interval); } }
System.Threading.Timer控件是一个用于非UI界面的小型定时器。 上一句就是对timer进行初始化 通过this.Interval(比如是10分钟,则10分钟后开始执行、每十分钟执行一次),TimerHandler是要执行的方法,代码如下:
private void TimerHandler(object state) { this._timer.Change(-1, -1); this.Run(); if (this.RunOnlyOnce) { this.Dispose(); } else { this._timer.Change(this.Interval, this.Interval); } }
this._timer.Change(-1, -1); 是重新设置执行周期,都设置为-1就是停止timer, 然后运行Run方法。 最后如果只执行一次 就调用dispose方法,停止运行,并清理。如果是否,则设置 timer恢复定时器运行。 dispose方法如下:
/// <summary> /// Disposes the instance /// </summary> public void Dispose() { if ((this._timer != null) && !this._disposed) { lock (this) { this._timer.Dispose(); this._timer = null; this._disposed = true; } } }
上面很简单,不用解释了。然后看RUN方法 代码如下:
private void Run() { if (Seconds <= 0) return; this.StartedUtc = DateTime.UtcNow; this.IsRunning = true; foreach (Task task in this._tasks.Values) { task.Execute(); } this.IsRunning = false; }
每隔一定时间运行的RUN方法。设置开始时间,设置是否正在运行为true,执行._tasks字典类型的所有任务的Execute方法。最后设置是否正在运行为false.
重点落在了Task.Execute上。找到这个类,代码如下:
namespace Nop.Services.Tasks { /// <summary> /// Task /// </summary> public partial class Task { /// <summary> /// Ctor for Task /// </summary> private Task() { this.Enabled = true; } /// <summary> /// Ctor for Task /// </summary> /// <param name="task">Task </param> public Task(ScheduleTask task) { this.Type = task.Type; this.Enabled = task.Enabled; this.StopOnError = task.StopOnError; this.Name = task.Name; } private ITask CreateTask(ILifetimeScope scope) { ITask task = null; if (this.Enabled) { var type2 = System.Type.GetType(this.Type); if (type2 != null) { object instance; if (!EngineContext.Current.ContainerManager.TryResolve(type2, scope, out instance)) { //not resolved instance = EngineContext.Current.ContainerManager.ResolveUnregistered(type2, scope); } task = instance as ITask; } } return task; } /// <summary> /// Executes the task /// </summary> /// <param name="throwException">A value indicating whether exception should be thrown if some error happens</param> /// <param name="dispose">A value indicating whether all instances hsould be disposed after task run</param> public void Execute(bool throwException = false, bool dispose = true) { this.IsRunning = true; //background tasks has an issue with Autofac //because scope is generated each time it's requested //that's why we get one single scope here //this way we can also dispose resources once a task is completed var scope = EngineContext.Current.ContainerManager.Scope(); var scheduleTaskService = EngineContext.Current.ContainerManager.Resolve<IScheduleTaskService>("", scope); var scheduleTask = scheduleTaskService.GetTaskByType(this.Type); try { var task = this.CreateTask(scope); if (task != null) { this.LastStartUtc = DateTime.UtcNow; if (scheduleTask != null) { //update appropriate datetime properties scheduleTask.LastStartUtc = this.LastStartUtc; scheduleTaskService.UpdateTask(scheduleTask); } //execute task task.Execute(); this.LastEndUtc = this.LastSuccessUtc = DateTime.UtcNow; } } catch (Exception exc) { this.Enabled = !this.StopOnError; this.LastEndUtc = DateTime.UtcNow; //log error var logger = EngineContext.Current.ContainerManager.Resolve<ILogger>("", scope); logger.Error(string.Format("Error while running the '{0}' schedule task. {1}", this.Name, exc.Message), exc); if (throwException) throw; } if (scheduleTask != null) { //update appropriate datetime properties scheduleTask.LastEndUtc = this.LastEndUtc; scheduleTask.LastSuccessUtc = this.LastSuccessUtc; scheduleTaskService.UpdateTask(scheduleTask); } //dispose all resources if (dispose) { scope.Dispose(); } this.IsRunning = false; } /// <summary> /// A value indicating whether a task is running /// </summary> public bool IsRunning { get; private set; } /// <summary> /// Datetime of the last start /// </summary> public DateTime? LastStartUtc { get; private set; } /// <summary> /// Datetime of the last end /// </summary> public DateTime? LastEndUtc { get; private set; } /// <summary> /// Datetime of the last success /// </summary> public DateTime? LastSuccessUtc { get; private set; } /// <summary> /// A value indicating type of the task /// </summary> public string Type { get; private set; } /// <summary> /// A value indicating whether to stop task on error /// </summary> public bool StopOnError { get; private set; } /// <summary> /// Get the task name /// </summary> public string Name { get; private set; } /// <summary> /// A value indicating whether the task is enabled /// </summary> public bool Enabled { get; set; } } }
可以看出Task是一个普通的类,没有任何集成,但他是一个部分类,这就要小心了,说不定别的地方也可以再写一个部分Task类,现在我们仅看这个。
里面包含了一些属性,包括:Name,Enabled,Type,isRunning等等。我们分析Execute方法。
开始获取依赖注入的 scope,应该是基于请求的(就不查了),然后根据当前SCOPE获得计划任务服务对象。用计划任务服务对象根据当前Task的类型获得计划任务(GetTaskByType)。
调用var task = this.CreateTask(scope);方法 获得ITask对象。代码如下:
private ITask CreateTask(ILifetimeScope scope) { ITask task = null; if (this.Enabled) { var type2 = System.Type.GetType(this.Type); if (type2 != null) { object instance; if (!EngineContext.Current.ContainerManager.TryResolve(type2, scope, out instance)) { //not resolved instance = EngineContext.Current.ContainerManager.ResolveUnregistered(type2, scope); } task = instance as ITask; } } return task; }
就是根据this.Type获得当前类型的一个实例。然后就是执行task.Execute();方法 当然还要设置一些最后开始时间、最后结束时间=最后成功运行时间 等 。最后scope.Dispose();释放scope.
下面是ITask接口的代码:
public partial interface ITask { /// <summary> /// Execute task /// </summary> void Execute(); }
回到Global.asax.cs 后面代码:就是调用调用日志 输出应用启动。结束~!!!!
//log application start if (databaseInstalled) { try { //log var logger = EngineContext.Current.Resolve<ILogger>(); logger.Information("Application started", null, null); } catch (Exception) { //don't throw new exception if occurs } }