分析 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
- }
- }
来源: http://www.bubuko.com/infodetail-3289828.html