• NopCommerce之任务执行


    NOP任务提供两种:手动执行(立即)和定时执行两种。

    首先来说下手动任务执行过程,下图是NOP定时任务管理界面:

    从上面可以看出,我们可以选择具体的任务来手动执行任务(立即执行),当点击【立即执行】按钮时会触发以下事件:

    1、获取ScheduleTask对象(从数据库或缓存中)

    2、创建Task对象,传入ScheduleTask对象作为构造参数,并为Task对象字段赋值

    3、执行Task里的Execute()方法,该方法主要负责ITask实现类的创建,并执行Execute()方法,完成对任务的执行。

    具体代码如下:

     1   public ActionResult RunNow(int id)
     2         {
     3             if (!_permissionService.Authorize(StandardPermissionProvider.ManageScheduleTasks))
     4                 return AccessDeniedView();
     5 
     6             try
     7             {
     8                 var scheduleTask = _scheduleTaskService.GetTaskById(id);
     9                 if (scheduleTask == null)
    10                     throw new Exception("Schedule task cannot be loaded");
    11 
    12                 var task = new Task(scheduleTask);
    13                 //ensure that the task is enabled
    14                 task.Enabled = true;
    15                 //do not dispose. otherwise, we can get exception that DbContext is disposed
    16                 task.Execute(true, false);
    17                 SuccessNotification(_localizationService.GetResource("Admin.System.ScheduleTasks.RunNow.Done"));
    18             }
    19             catch (Exception exc)
    20             {
    21                 ErrorNotification(exc);
    22             }
    23 
    24             return RedirectToAction("List");
    25         }

    第8行:获取ScheduleTask对象(从数据库或缓存中)

    第16行:、创建Task对象,传入ScheduleTask对象作为构造参数,并为Task对象字段赋值,然后我们进入到该方法体内看下

     1  public void Execute(bool throwException = false, bool dispose = true)
     2         {
     3             this.IsRunning = true;
     4 
     5             //background tasks has an issue with Autofac
     6             //because scope is generated each time it's requested
     7             //that's why we get one single scope here
     8             //this way we can also dispose resources once a task is completed
     9             var scope = EngineContext.Current.ContainerManager.Scope();
    10             var scheduleTaskService = EngineContext.Current.ContainerManager.Resolve<IScheduleTaskService>("", scope);
    11             var scheduleTask = scheduleTaskService.GetTaskByType(this.Type);
    12 
    13             try
    14             {
    15                 var task = this.CreateTask(scope);
    16                 if (task != null)
    17                 {
    18                     this.LastStartUtc = DateTime.UtcNow;
    19                     if (scheduleTask != null)
    20                     {
    21                         //update appropriate datetime properties
    22                         scheduleTask.LastStartUtc = this.LastStartUtc;
    23                         scheduleTaskService.UpdateTask(scheduleTask);
    24                     }
    25 
    26                     //execute task
    27                     task.Execute();
    28                     this.LastEndUtc = this.LastSuccessUtc = DateTime.UtcNow;
    29                 }
    30             }
    31             catch (Exception exc)
    32             {
    33                 this.Enabled = !this.StopOnError;
    34                 this.LastEndUtc = DateTime.UtcNow;
    35 
    36                 //log error
    37                 var logger = EngineContext.Current.ContainerManager.Resolve<ILogger>("", scope);
    38                 logger.Error(string.Format("Error while running the '{0}' schedule task. {1}", this.Name, exc.Message), exc);
    39                 if (throwException)
    40                     throw;
    41             }
    42 
    43             if (scheduleTask != null)
    44             {
    45                 //update appropriate datetime properties
    46                 scheduleTask.LastEndUtc = this.LastEndUtc;
    47                 scheduleTask.LastSuccessUtc = this.LastSuccessUtc;
    48                 scheduleTaskService.UpdateTask(scheduleTask);
    49             }
    50 
    51             //dispose all resources
    52             if (dispose)
    53             {
    54                 scope.Dispose();
    55             }
    56 
    57             this.IsRunning = false;
    58         }

     第15行:ITask实现类的创建,里面可能包括反射、关联对象的一些创建

    第27行:执行实现了ITask类的Execute()方法,完成对任务的执行。最后更新字段。

     二、第二种就是定时任务了,相对来说复杂点。

    首先、在项目启动时的Application_Start()方法里,我们创建了任务管理类,

     1     protected void Application_Start()
     2         {
     3            
     4 
     5             //start scheduled tasks
     6             if (databaseInstalled)
     7             {
     8                 TaskManager.Instance.Initialize();
     9                 TaskManager.Instance.Start();
    10             }
    11 }

    该类主要负责任务的初始化,添加到线程列表,任务的开始和停止,如下:

     1  /// <summary>
     2         /// Initializes the task manager with the property values specified in the configuration file.
     3         /// </summary>
     4         public void Initialize()
     5         {
     6             this._taskThreads.Clear();
     7 
     8             var taskService = EngineContext.Current.Resolve<IScheduleTaskService>();
     9             var scheduleTasks = taskService
    10                 .GetAllTasks()
    11                 .OrderBy(x => x.Seconds)
    12                 .ToList();
    13 
    14             //group by threads with the same seconds
    15             foreach (var scheduleTaskGrouped in scheduleTasks.GroupBy(x => x.Seconds))
    16             {
    17                 //create a thread
    18                 var taskThread = new TaskThread
    19                                      {
    20                                          Seconds = scheduleTaskGrouped.Key
    21                                      };
    22                 foreach (var scheduleTask in scheduleTaskGrouped)
    23                 {
    24                     var task = new Task(scheduleTask);
    25                     taskThread.AddTask(task);
    26                 }
    27                 this._taskThreads.Add(taskThread);
    28             }
    29 
    30             //sometimes a task period could be set to several hours (or even days).
    31             //in this case a probability that it'll be run is quite small (an application could be restarted)
    32             //we should manually run the tasks which weren't run for a long time
    33             var notRunTasks = scheduleTasks
    34                 //find tasks with "run period" more than 30 minutes
    35                 .Where(x => x.Seconds >= _notRunTasksInterval)
    36                 .Where(x => !x.LastStartUtc.HasValue || x.LastStartUtc.Value.AddSeconds(x.Seconds) < DateTime.UtcNow)
    37                 .ToList();
    38             //create a thread for the tasks which weren't run for a long time
    39             if (notRunTasks.Count > 0)
    40             {
    41                 var taskThread = new TaskThread
    42                 {
    43                     RunOnlyOnce = true,
    44                     Seconds = 60 * 5 //let's run such tasks in 5 minutes after application start
    45                 };
    46                 foreach (var scheduleTask in notRunTasks)
    47                 {
    48                     var task = new Task(scheduleTask);
    49                     taskThread.AddTask(task);
    50                 }
    51                 this._taskThreads.Add(taskThread);
    52             }
    53         }

    代码分析如下:

    1、清空任务线程,

    2、获取数据库内所有的任务

    3、创建任务线程对象

    4、创建Task对象

    5、将Task对象存入 Dictionary<string, Task> 包装的集合中

    6、最后将线程对象taskThread存入到 由List<TaskThread>包装集合中,该集合保存了所有任务的线程,

    上面执行我们将所有需要执行的任务存入到List<TaskThread>包装集合中集合,我们下面就可以针对这些任务,执行定时任务,我们看下TaskManager类中Start()方法是如何写的:

    1        public void Start()
    2         {
    3             foreach (var taskThread in this._taskThreads)
    4             {
    5                 taskThread.InitTimer();
    6             }
    7         }

    上面的代码可以看出,NOP是循环线程集合中所有任务,并执行定时任务的。

    1   public void InitTimer()
    2         {
    3             if (this._timer == null)
    4             {
    5                 this._timer = new Timer(new TimerCallback(this.TimerHandler), null, this.Interval, this.Interval);
    6             }
    7         }

    上面的代码创建了Timer对象,在定时的时间内会执行TimerHandler()方法体中内容,方法体里的内容如下:

     1   private void TimerHandler(object state)
     2         {
     3             this._timer.Change(-1, -1);
     4             this.Run();
     5             if (this.RunOnlyOnce)
     6             {
     7                 this.Dispose();
     8             }
     9             else
    10             {
    11                 this._timer.Change(this.Interval, this.Interval);
    12             }
    13         }

    在方法体中会有个Run()方法,该方法会执行当前任务类中的Execute()方法,代码如下:

     1    private void Run()
     2         {
     3             if (Seconds <= 0)
     4                 return;
     5 
     6             this.StartedUtc = DateTime.UtcNow;
     7             this.IsRunning = true;
     8             foreach (Task task in this._tasks.Values)
     9             {
    10                 task.Execute();
    11             }
    12             this.IsRunning = false;
    13         }

    这样就实现了定时执行任务功能。。。

    由于很少写博客,并且功力有限。只能这样了。。。欢迎指正

    
    
  • 相关阅读:
    网上图书商城项目学习笔记-001工具类测试
    SpringMVC,MyBatis商品的增删改查
    3.Spring-用反射模拟IoC
    2.Sprng-IoC-Java反射例子
    1.Spring IoC简单例子
    HTML5 文件API(二)
    HTML5 文件API(一)
    HTML5 编辑 API 之 Range 对象(二)
    hdu 5384 Danganronpa
    hdu 3065 病毒侵袭持续中
  • 原文地址:https://www.cnblogs.com/xujie520/p/5150433.html
Copyright © 2020-2023  润新知