C#中如何实现计划任务?在复杂的应用程序中有时候会要求一个或多个任务在一定时间或者间隔时间内计划进行,比如备份数据库,定时发送电子邮件,清空数据等等。实现计划任务有很多方式,可以用SQLAgent执行存储过程来实现,也可以用Windows计划任务来实现。对于Web应用程序来说,实现起来不是很简单,本文下面介绍两种在Web应用程序中是现计划任务的方法。
由于ASP.NET站点是作为Web应用程序运行的,它并不受线程的限制,因此我们可以非常方便地在Application_Start和Application_End事件中建立和销毁一个计划任务。下面就简单介绍一下在Web站点实现计划任务的方法。我们的例子是定时往文件里添加信息,作为例子,这里把当前的时间定时地写入文件中。
一个计划任务的工作单元称之为一个任务(Job),下面的代码描述了对所有任务都可以被调度引擎计划执行的一个通用的接口,这里的每个任务实现了Execute方法,供调度引擎进行调用:
public interface ISchedulerJob { void Execute(); }
如前所述,我们的例子是实现往文件写如字符日期,下面就是实现这一任务的方法:
public class SampleJob : ISchedulerJob { public void Execute() { //文件保存的物理路径,CSTest为虚拟目录名称,F:\Inetpub\wwwroot\CSTest为物理路径 string p = @"F:\Inetpub\wwwroot\CSTest"; //我们在虚拟目录的根目录下建立SchedulerJob文件夹,并设置权限为匿名可修改, //SchedulerJob.txt就是我们所写的文件 string FILE_NAME = p+ "http://www.cnblogs.com/dajiang02/admin/file://schedulerjob//SchedulerJob.txt"; //取得当前服务器时间,并转换成字符串 string c = System.DateTime.Now.ToString("yyyy-mm-dd hh:MM:ss"); //标记是否是新建文件的标量 bool flag = false; //如果文件不存在,就新建该文件 if (!File.Exists(FILE_NAME)) { flag = true; StreamWriter sr = File.CreateText(FILE_NAME); sr.Close(); } //向文件写入内容 StreamWriter x = new StreamWriter(FILE_NAME,true,System.Text.Encoding.Default); if(flag) x.Write("计划任务测试开始:"); x.Write("\r\n"+c); x.Close(); } }
接下来,我们建立一个配置对象,告诉调度引擎执行什么任务和执行的时间间隔。
public class SchedulerConfiguration { //时间间隔 private int sleepInterval; //任务列表 private ArrayList jobs = new ArrayList(); public int SleepInterval { get { return sleepInterval; } } public ArrayList Jobs { get { return jobs; } } //调度配置类的构造函数 public SchedulerConfiguration(int newSleepInterval) { sleepInterval = newSleepInterval; } }
下面就是调度引擎,定时执行配置对象的任务
public class Scheduler { private SchedulerConfiguration configuration = null; public Scheduler(SchedulerConfiguration config) { configuration = config; } public void Start() { while(true) { //执行每一个任务 foreach(ISchedulerJob job in configuration.Jobs) { ThreadStart myThreadDelegate = new ThreadStart(job.Execute); Thread myThread = new Thread(myThreadDelegate); myThread.Start(); Thread.Sleep(configuration.SleepInterval); } } } }
所有的准备工作已经完成,下面就是激活引擎的工作了。为了让我们的任务计划执行,我们在Global.asax.cs文件里的Applicatio_Start和Application_End里进行建立和销毁工作,首先建立一个调度进程运行的线程,我们这里的运行间隔时间为3秒钟。
public System.Threading.Thread schedulerThread = null; protected void Application_Start(Object sender, EventArgs e) { SchedulerConfiguration config = new SchedulerConfiguration(1000*3); config.Jobs.Add(new SampleJob()); Scheduler scheduler = new Scheduler(config); System.Threading.ThreadStart myThreadStart = new System.Threading.ThreadStart(scheduler.Start); System.Threading.Thread schedulerThread = new System.Threading.Thread(myThreadStart); schedulerThread.Start(); } 最后还需要在程序退出时进行销毁: protected void Application_End(Object sender, EventArgs e) { if (null != schedulerThread) { schedulerThread.Abort(); } }
好了,在VS.NET里建立一个C#的Web应用程序工程,建立TaskScheduler.cs类,并修改相应的Global.asax.cs文件。为了能看到效果,我们再建立一个表单WebForm1.aspx,定时刷新来检查我们所记录的数据:
<%@ Page language="c#" Codebehind="WebForm1.aspx.cs" AutoEventWireup="false" Inherits="CSTest.WebForm1" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" > <HTML> <HEAD> <title>在Web应用程序中执行计划任务的例子</title> <meta http-equiv="refresh" content="10"> <meta name="GENERATOR" Content="Microsoft Visual Studio 7.0"> <meta name="CODE_LANGUAGE" Content="C#"> <meta name="vs_defaultClientScript" content="JavaScript"> <meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5"> </HEAD> <body MS_POSITIONING="GridLayout"> <form id="Form1" method="post" runat="server"> <iframe style="100%;height:100%" src="SchedulerJob/SchedulerJob.txt"></iframe> </form> </body> </HTML>
对工程进行编译并运行,就可以看到结果了,结果如下:
计划任务测试开始:
2003-13-10 11:08:15
2003-13-10 11:08:18
2003-13-10 11:08:21
2003-13-10 11:08:24
2003-13-10
11:08:27
2003-13-10 11:08:30
需要说明的是,以上只是在Web应用程序中执行计划任务的简单例子,对于多个任务来说,需要在不同的线程内进行工作,对计划的安排也是很简单的,实际还需要站点堵塞,当机的情况。另外这里也没有进行错误的处理等工作,相信大家会写出更加完美的代码的。
下面的是可执行多任务,隔时间执行的计划任务。(与上面的不是同一种方法)
using System.Collections; using System.Threading; namespace Metrial { /// <summary> /// TaskScheduler 的摘要说明 /// </summary> public class TaskScheduler { /// <summary> /// 执行方法接口 /// </summary> public interface ISchedulerJob { /// <summary> /// 执行方法 /// </summary> void Execute(); } /// <summary> /// 任务sample1 /// </summary> public class Method1 : ISchedulerJob { public void Execute() { //需要定时执行的方法1 } } /// <summary> /// 任务sample2 /// </summary> public class Method2 : ISchedulerJob { public void Execute() { //需要定时执行的方法2 } } // 接下来,我们建立一个配置对象,告诉调度引擎执行什么任务和执行的时间间隔。 /// <summary> /// /// </summary> public class SchedulerConfiguration { //时间间隔 //任务列表 /// <summary> /// /// </summary> private readonly ArrayList jobs = new ArrayList(); /// <summary> /// 线程睡眠时间设置 /// </summary> /// <value>The sleep interval.</value> public int SleepInterval { get; private set; } /// <summary> /// 需完成的工作列表 /// </summary> /// <value>The jobs.</value> public ArrayList Jobs { get { return jobs; } } public SchedulerConfiguration(int newSleepInterval) { SleepInterval = newSleepInterval; } } //下面就是调度引擎,定时执行配置对象的任务 /// <summary> /// /// </summary> public class Scheduler { /// <summary> /// /// </summary> private readonly SchedulerConfiguration configuration; /// <summary> /// Initializes a new instance of the <see cref="Scheduler"/> class. /// </summary> /// <param name="config">The config.</param> public Scheduler(SchedulerConfiguration config) { configuration = config; } /// <summary> /// Starts this instance. /// </summary> public void Start() { while (true) { //执行每一个任务 foreach (ISchedulerJob job in configuration.Jobs) { var myThreadDelegate = new ThreadStart(job.Execute); var myThread = new Thread(myThreadDelegate); myThread.Start(); } Thread.Sleep(configuration.SleepInterval); } } } } }
最后在Global.asax的Application_Start事件中调用Task方法。
public void Task() { var config = new TaskScheduler.SchedulerConfiguration(1000 * 60 * 10); config.Jobs.Add(new TaskScheduler.Method1()); config.Jobs.Add(new TaskScheduler.Method2()); var scheduler = new TaskScheduler.Scheduler(config); var myThreadStart = new System.Threading.ThreadStart(scheduler.Start); var schedulerThread = new System.Threading.Thread(myThreadStart); schedulerThread.Start(); }
另外一个严重问题是:IIS运行到一定时期,计划任务就自动停止了。原因是IIS应用程序池的定期回收导致计划任务的自动停止。可以在Application_End中添加下面代码:
//下面的代码是关键,可解决IIS应用程序池自动回收的问题 Thread.Sleep(1000); //这里设置你的web地址,可以随便指向你的任意一个aspx页面甚至不存在的页面,目的是要激发Application_Start string url = "http://localhost:82/111.aspx"; HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(url); HttpWebResponse myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse(); Stream receiveStream = myHttpWebResponse.GetResponseStream();//得到回写的字节流
原理:Global.asax 可以是asp.net中应用程序或会话事件处理程序,我们用到了Application_Start(应用程序开始事件)和 Application_End(应用程序结束事件)。当应用程序开始时,启动一个定时器,用来定时执行任务YourTask()方法,这个方法里面可以写上需要调用的逻辑代码,可以是单线程和多线程。当应用程序结束时,如IIS的应用程序池回收,让asp.net去访问当前的这个web地址,本地测试的时候写本地地址,如http://localhost:82/111.aspx,到正式生产环境,可以改成你的web地址。这里需要访问一个aspx页面,这样就可以重新激活应用程序。
PS:参考了很多种方法,也整理了很多代码和前辈们的经验,这篇文章是我挑选的三篇认为比较好的并且私有化后的结果,整理的代码在测试后就删了,现在就是直接把代码给Ctrl过来的,希望多大家有所帮助。