一、使用说明
-
把要执行的任务新建一个类放到本Web项目中,可以放在任意位置,建议放在AutoTask 文件夹下
-
对该类应用属性 AutoTask 如下所示
[AutoTask(EnterMethod = “StartTask”, IntervalSeconds = 86400, StartTime = “2016-12-28 00:00:00”)]
说明:EnterMethod 是任务入口,IntervalSeconds 是执行间隔,StartTime 是开始执行时间
如果设置IntervalSeconds 为空或0 ,则该任务只启动时执行一次,忽略后面的start Time 参数
如果没有设置StartTime ,则该任务启动时立刻开始执行,按照间隔时间,每隔IntervalSeconds 秒执行一次。
如果StartTime 大于当前日期,则任务从StartTime 开始执行。
如果StartTime 小于当前日期,则从 StartTime + IntervalSecondes*n 开始执行,n为使该表达式大于DateTime.Now 的最小值。 -
由此可以实现所有的定时功能。
如指定 每小时执行,则设置IntervalSeconds=3600,每天执行则设置IntervalSeconds=86400
若设置每天 0 点执行,则可以设置StartTime = “xxxx-xx-xx 00:00:00”,IntervalSeconds=86400
若设置每周二晚上23 点执行 ,则可以设置StartTime = “xxxx-xx-xx 23:00:00”,IntervalSeconds=604800 //86400*7 ,xxxx-xx-xx 可以设置为以前一个周二的日期
若设置每月,每年指定日期,则可以设置每天执行,然后在执行的代码里进行判断,判断日期是否是指定的日期,为保证易用性,本类库不进行扩展。
二、源码说明
- 在Global.asax.cs Application_Start中添加如下代码
AutoTaskAttribute.RegisterTask();
添加Application_End 方法,解决IIS应用程序池自动回收的问题。
protected void Application_End(object sender, EventArgs e)
{
//下面的代码是关键,可解决IIS应用程序池自动回收的问题
Thread.Sleep(1000);
//这里设置你的web地址,可以随便指向你的任意一个aspx页面甚至不存在的页面,目的是要激发Application_Start <br> //我这里是一个验证token的地址。
string url = System.Configuration.ConfigurationManager.AppSettings["tokenurl"];
HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(url);
HttpWebResponse myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
Stream receiveStream = myHttpWebResponse.GetResponseStream();//得到回写的字节流
}
这段代码在使用时有一个问题,就是由于服务器不会回收,所以如果你修改你的网页,然后在浏览器中浏览,查看源代码会发现代码没有被修改,需要将这段代码注释才能正常
- 把该类文件添加到项目中
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Web;
namespace TimerMvcWeb.Filters
{
/// <summary>
/// Author:BigLiang(lmw)
/// Date:2016-12-29
/// </summary>
[AttributeUsage(AttributeTargets.Class)]//表示此Attribute仅可以施加到类元素上
public class AutoTaskAttribute : Attribute
{
/// <summary>
/// 入口程序
/// </summary>
public string EnterMethod { get; set; }
/// <summary>
/// 执行间隔秒数(未设置或0 则只执行一次)
/// </summary>
public int IntervalSeconds { get; set; }
/// <summary>
/// 开始执行日期
/// </summary>
public string StartTime { get; set; }
//保留对Timer 的引用,避免回收
private static Dictionary<AutoTaskAttribute, System.Threading.Timer> timers = new Dictionary<AutoTaskAttribute, System.Threading.Timer>();
/// <summary>
/// Global.asax.cs 中调用
/// </summary>
public static void RegisterTask()
{
//异步执行该方法
new Task(() => StartAutoTask()).Start();
}
/// <summary>
/// 启动定时任务
/// </summary>
private static void StartAutoTask()
{
var types = Assembly.GetExecutingAssembly().ExportedTypes.Where(t => Attribute.IsDefined(t, typeof(AutoTaskAttribute))).ToList();
foreach (var t in types)
{
try
{
var att = (AutoTaskAttribute)Attribute.GetCustomAttribute(t, typeof(AutoTaskAttribute));
if (att != null)
{
if (string.IsNullOrWhiteSpace(att.EnterMethod))
{
throw new Exception("未指定任务入口!EnterMethod");
}
var ins = Activator.CreateInstance(t);
var method = t.GetMethod(att.EnterMethod);
if (att.IntervalSeconds > 0)
{
int duetime = 0; //计算延时时间
if (string.IsNullOrWhiteSpace(att.StartTime))
{
duetime = 1000;
}
else
{
var datetime = DateTime.Parse(att.StartTime);
if (DateTime.Now <= datetime)
{
duetime = (int)(datetime - DateTime.Now).TotalSeconds * 1000;
}
else
{
duetime = att.IntervalSeconds * 1000 - ((int)(DateTime.Now - datetime).TotalMilliseconds) % (att.IntervalSeconds * 1000);
}
}
timers.Add(att, new System.Threading.Timer((o) =>
{
method.Invoke(ins, null);
}, ins, duetime, att.IntervalSeconds * 1000));
}
else
{
method.Invoke(ins, null);
}
}
}
catch (Exception ex)
{
//LogHelper.Error(t.FullName + " 任务启动失败", ex);
Debug.WriteLine(t.FullName + " 任务启动失败", ex);
}
}
}
}
}
- 使用示例
/// <summary>
/// 测试任务,从程序启动开始,每1秒执行一次
/// </summary>
[AutoTask(EnterMethod = "StartTask", IntervalSeconds = 1, StartTime = "")]
public class TestTask
{
public static int count = 0;
public static void StartTask()
{
Debug.WriteLine(count+"定时任务启动成功!");
count++;
}
}