有没有什么聪明的方法可以让我的 executeEveryDayMethod() 每天执行一次,而不必涉及 Windows TaskScheduler?
10 回答
我通过执行以下操作实现了这一点......
- 设置一个每 20 分钟触发一次的计时器(尽管实际时间取决于您——我需要在一天中多次运行)。
- 在每个 Tick 事件上,检查系统时间。将时间与您的方法的计划运行时间进行比较。
- 如果当前时间小于计划时间,则在某个持久存储中检查 a 以获取该方法最后一次运行的日期时间值。
- 如果该方法上次运行时间超过 24 小时,请运行该方法,并将此运行的日期时间存储回您的数据存储
- 如果该方法最后一次运行是在过去 24 小时内,请忽略它。
高温高压
*edit - C# 中的代码示例 :: 注意:未经测试...
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);
}
}
public partial class Main : Form {
public Main( ) // Windows Form 被调用 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;
}
}
如果您在某个时间范围内,您可以查询时间并运行,这样即使机器关闭,您也会调用该方法或使用 Vinko 建议的计时器。
但是更好的解决方案(类似于旧的 CRON 版本,因此它是一种经过验证的模式)是拥有一些持久性数据,我现在能想到的最便宜的解决方案是一个空白文件,检查其最后修改的属性,如果它没有t 在过去 24 小时内被修改,您触摸它并运行您的方法。通过这种方式,您可以确保在应用程序周末外出的情况下首先运行该方法。
我以前在 C# 中做过这个,但它是一年前的另一个工作,所以我没有代码,但它大约有 20 行(带有注释和所有)左右。
如果计算机重新启动可能会出现问题,因此您可以将应用程序作为 Windows 服务运行。
要在每天晚上 7 点到 8 点之间运行一次作业,我设置了一个间隔 = 3600000 毫秒的计时器,然后只需执行以下代码进行计时器滴答。
private void timer1_Tick(object sender, EventArgs e)
{
//ensure that it is running between 7-8pm daily.
if (DateTime.Now.Hour == 19)
{
RunJob();
}
}
一个小时的窗口对我来说很好。时间上的额外粒度将需要更小的计时器间隔(一分钟 60000),包括 if 的分钟数。
例如
{
//ensure that it is running at 7:30pm daily.
if (DateTime.Now.Hour == 19 && DateTime.Now.Minute == 30)
{
RunJob();
}
}
如果您只想每天运行一次并且不在乎何时运行,这将起作用(将在午夜之后运行)。
声明一个DateTime
变量:
DateTime _DateLastRun;
在您的启动中,设置初始日期值:
_DateLastRun = DateTime.Now.Date;
在要检查是否执行该操作的逻辑区域中:
if (_DateLastRun < DateTime.Now.Date)
{
// Perform your action
_DateLastRun= DateTime.Now.Date;
}
你可以试试这个解决方案。
public Main()
{
StartService();
}
public async Task StartService(CancellationToken token = default(CancellationToken))
{
while (!token.IsCancellationRequested)
{
ExecuteFunction();
try
{
await Task.Delay(TimeSpan.FromDays(1), token);
}
catch (TaskCanceledException)
{
break;
}
}
}
public async Task ExecuteFunction()
{
...
}
如果您正在运行 Windows 窗体应用程序,以下是您可以执行的操作。但是您需要配置一个设置,以便您可以存储触发事件的最后日期。如果您从不打算关闭应用程序,您可以将日期存储为静态值。
我使用计时器来触发事件,如下所示:
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
}
}