有没有巧妙的方法可以让我的executeEveryDayMethod()每天执行一次,而无需涉及Windows任务计划程序?
using System;
using System.Collections.Generic;
using System.Text;
using System.Timers;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
Timer t1 = new Timer();
t1.Interval = (1000 * 60 * 20); // 20 minutes...
t1.Elapsed += new ElapsedEventHandler(t1_Elapsed);
t1.AutoReset = true;
t1.Start();
Console.ReadLine();
}
static void t1_Elapsed(object sender, ElapsedEventArgs e)
{
DateTime scheduledRun = DateTime.Today.AddHours(3); // runs today at 3am.
System.IO.FileInfo lastTime = new System.IO.FileInfo(@"C:\lastRunTime.txt");
DateTime lastRan = lastTime.LastWriteTime;
if (DateTime.Now > scheduledRun)
{
TimeSpan sinceLastRun = DateTime.Now - lastRan;
if (sinceLastRun.Hours > 23)
{
doStuff();
// Don't forget to update the file modification date here!!!
}
}
}
static void doStuff()
{
Console.WriteLine("Running the method!");
}
}
}
请看 quartz.net。它是用于 .net 的调度库。
更具体的内容请参见这里。
这里有一个相关问题(示例取自其中)。
using System;
using System.Timers;
public class Timer1
{
private static Timer aTimer = new System.Timers.Timer(24*60*60*1000);
public static void Main()
{
aTimer.Elapsed += new ElapsedEventHandler(ExecuteEveryDayMethod);
aTimer.Enabled = true;
Console.WriteLine("Press the Enter key to exit the program.");
Console.ReadLine();
}
// Specify what you want to happen when the Elapsed event is
// raised.
private static void ExecuteEveryDayMethod(object source, ElapsedEventArgs e)
{
Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime);
}
}
//note the namespace, there are 4 different timers in .NET
System.Threading.Timer _Timer;
DateTime now = DateTime.Now;
//convert "hh:mm:ss" to three integers
var dateparts = _Settings.DataCleanupTime.Split(new char[] { ':' }).Select(p => Convert.ToInt32(p)).ToArray();
DateTime firstTime = new DateTime(now.Year, now.Month, now.Day, dateparts[0], dateparts[1], dateparts[2]);
//e.g. firsttime is today at 2am and it is already 6am
if(firstTime < now)
{
//first run will be tomorrow
firstTime = firstTime.AddDays(1);
}
int delay = Convert.ToInt32((firstTime - now).TotalMilliseconds);
_Timer = new Timer(DoWork, state:null, delay, 3600 * 24 * 1000);
DoWork的签名是:
public void DoWork(Object state)
要停止计时器,只需调用:
_Timer.Dispose();
公共部分类 Main :Form
{
public Main( ) // Windows 窗体称为 Main
{
InitializeComponent( );
}
private void Main_Load( object sender, EventArgs e )
{
/*
This example uses a System.Windows.Forms Timer
This code allows you to schedule an event at any given time in one day.
In this example the timer will tick at 3AM.
*/
Int32 alarm = GetAlarmInMilliseconds( 3, 0, 0 ); // Milliseconds until 3:00 am.
timer_MessageCount.Interval = alarm; // Timer will tick at 3:00am.
timer_MessageCount.Start( );
}
private Int32 GetAlarmInMilliseconds(Int32 eventHour, Int32 eventMinute, Int32 eventSecond )
{
DateTime now = DateTime.Now;
DateTime eventTime = new DateTime( now.Year, now.Month, now.Day, eventHour, eventMinute, eventSecond );
TimeSpan ts;
if ( eventTime > now )
{
ts = eventTime - now;
}
else
{
eventTime = eventTime.AddDays( 1 );
ts = eventTime - now;
}
Console.WriteLine("Next alarm in: {0}", ts );
return ( Int32 ) ts.TotalMilliseconds;
}
static void DoSomething( )
{
Console.WriteLine( "Run your code here." );
}
private void timer_MessageCount_Tick( object sender, EventArgs e )
{
DoSomething( );
Int32 alarm = GetAlarmInMilliseconds( 3, 0, 0 ); // Next alarm time = 3AM
timer_MessageCount.Interval = alarm;
}
}
这是我非常简单的解决方案,用于每天执行一次方法:
private static DateTime _LastAccessedTime;
private static void OnceADayCode() // method that you want access once a day
{
_LastAccessedTime = DateTime.Today;
}
public static void PublicMethod() // this can be a method called from outside
{
if (_LastAccessedTime != DateTime.Today)
{
OnceADayCode();
}
}
使用相同的逻辑,您也可以使用:
private static DateTime _LastAccessedTime;
private static void OnceADayCode()
{
if (_LastAccessedTime.Today != DateTime.Today)
{
// code that you want access once a day
_LastAccessedTime = DateTime.Today;
}
}
如果你在某个时间范围内查询时间并运行,即使机器关闭,你也会调用该方法或使用像Vinko建议的计时器。
但更好的解决方案(类似于旧版CRON,因此它是一种经过验证的模式)是具有一些持久数据,我现在能想到的最便宜的解决方案是一个空文件,检查其上次修改属性,如果它在过去24小时内没有被修改,则触摸它并运行您的方法。这样,您可以确保在应用程序例如周末停止运行的情况下首先运行该方法。
我以前在C#中做过这件事,但那是一年前在另一份工作中完成的,所以我没有代码,但大约有20行(包括所有注释)。
DateTime
变量:DateTime _DateLastRun;
_DateLastRun = DateTime.Now.Date;
在逻辑区域中,您想要检查是否执行操作:
if (_DateLastRun < DateTime.Now.Date)
{
// Perform your action
_DateLastRun= DateTime.Now.Date;
}
private void timer1_Tick(object sender, EventArgs e)
{
//ensure that it is running between 7-8pm daily.
if (DateTime.Now.Hour == 19)
{
RunJob();
}
}
对我来说,一个小时的时间窗口就可以了。如果需要更精细的时间间隔,则需要在计时器上设置更小的间隔(例如一分钟的60000毫秒),并在条件语句中包含分钟。
例如:
{
//ensure that it is running at 7:30pm daily.
if (DateTime.Now.Hour == 19 && DateTime.Now.Minute == 30)
{
RunJob();
}
}
如果您正在运行 Windows Forms 应用程序,以下是如何执行操作的方法。但您需要配置一个设置以便可以存储事件最后触发的日期。如果您从不打算关闭应用程序,可以将日期存储为静态值。
我正在使用计时器来触发事件,如下所示:
private void tmrAutoBAK_Tick(object sender, EventArgs e)
{
if (BakDB.Properties.Settings.Default.lastFireDate != DateTime.Now.ToString("yyyy-MM-dd"))
{
tmrAutoBAK.Stop(); //STOPS THE TIMER IN CASE OF EVENTUAL MESSAGEBOXES.
createBakup(); //EVENT
BakDB.Properties.Settings.Default.lastFireDate = DateTime.Now.ToString("yyyy-MM-dd"); //STORING CURRENT DATE TO SETTINGS FILE.
BakDB.Properties.Settings.Default.Save(); //SAVING THE SETTING FILE.
tmrAutoBAK.Start(); //RESTARTING TIMER
}
}
DateTime.UtcNow
来避免夏令时问题。 - Orace