如何在C#中的特定时间每天调用一个方法?
我search了这个,find了关于Quartz.net的答案。 但是对我的项目来说似乎太大了。 我想要一个等效的解决scheme,但更简单和(最好)代码(不需要外部库)。 我如何在特定的时间每天调用一个方法?
我需要添加一些关于这个的信息:
- 最简单(也是丑陋的)的方法是在适当的时候每秒钟检查一次并调用方法
我想要一个更有效的方法来做到这一点,不需要经常检查时间,而且我可以控制工作是否完成了。 如果方法失败(由于任何问题),程序应该知道要写入日志/发送电子邮件。 这就是为什么我需要调用方法,而不是安排工作。
我发现这个解决scheme在Java中以固定的时间在Java中调用方法 。 在C#中有类似的方法吗?
编辑:我已经这样做了。 我在void Main()中添加了一个参数,并且创build了一个bat(由Windows Task Scheduler调度)来运行带有这个参数的程序。 程序运行,完成工作,然后退出。 如果一项工作失败,它能够写日志和发送电子邮件。 这种方法适合我的要求:)
- 创build一个控制台应用程序,做你正在寻找的
- 使用Windows“ 计划任务 ”function,可以在您需要的时候执行该控制台应用程序
这真是你所需要的!
更新:如果你想在你的应用程序中做到这一点,你有几个select:
- 在Windows窗体应用程序中,您可以使用
Application.Idle
事件并检查是否已经到达当天调用方法的时间。 只有当您的应用程序不忙于其他的东西时,才会调用此方法。 快速检查,看看你的目标时间已经达到不应该太多的压力,你的应用程序,我认为… - 在ASP.NET Web应用程序中,有“模拟”发送预定事件的方法 – 查看此CodeProject文章
- 当然,您也可以在任何.NET应用程序中简单地“滚动自己” – 查看此CodeProject文章以获取示例实现
更新#2 :如果你想每60分钟检查一次,你可以创build一个计时器,每60分钟唤醒一次,如果时间到了,就调用该方法。
像这样的东西:
using System.Timers; const double interval60Minutes = 60 * 60 * 1000; // milliseconds to one hour Timer checkForTime = new Timer(interval60Minutes); checkForTime.Elapsed += new ElapsedEventHandler(checkForTime_Elapsed); checkForTime.Enabled = true;
然后在你的事件处理程序中:
void checkForTime_Elapsed(object sender, ElapsedEventArgs e) { if (timeIsReady()) { SendEmail(); } }
正如其他人所说,您可以使用控制台应用程序按计划运行。 别人没有说的是,你可以通过这个应用程序触发你在主应用程序中等待的跨进程EventWaitHandle。
控制台程序:
class Program { static void Main(string[] args) { EventWaitHandle handle = new EventWaitHandle(true, EventResetMode.ManualReset, "GoodMutexName"); handle.Set(); } }
主应用程序:
private void Form1_Load(object sender, EventArgs e) { // Background thread, will die with application ThreadPool.QueueUserWorkItem((dumby) => EmailWait()); } private void EmailWait() { EventWaitHandle handle = new EventWaitHandle(false, EventResetMode.ManualReset, "GoodMutexName"); while (true) { handle.WaitOne(); SendEmail(); handle.Reset(); } }
每当我构build需要这种function的应用程序时,我总是通过一个简单的.NET库来使用Windows任务计划程序 。
有关示例代码和更多解释, 请参阅我对类似问题的回答 。
我知道的最好的方法可能是最简单的方法是使用Windows任务计划程序在一天中的特定时间执行代码,或让应用程序永久运行并检查一天的特定时间,或者编写一个Windows服务相同。
我知道这是旧的,但如何呢:
build立一个计时器在启动时触发,计算下一次运行时间。 在运行时的第一个调用中,取消第一个定时器并启动一个新的每日定时器。 每天更改为小时或任何你想要的周期性。
我创build了一个易于使用的简单调度程序,您不需要使用外部库。 TaskScheduler是一个单一的引用定时器,所以定时器不会被垃圾收集,它可以安排多个任务。 你可以设置第一次运行(小时和分钟),如果在这个时间安排在这个时间的第二天这个时间已经结束了。 但是定制代码很容易。
安排新任务非常简单。 例如:11:52第一个任务是每15个分支,第二个分支是每个5个分支。 对于日常执行设置24到3参数。
TaskScheduler.Instance.ScheduleTask(11, 52, 0.00417, () => { Debug.WriteLine("task1: " + DateTime.Now); //here write the code that you want to schedule }); TaskScheduler.Instance.ScheduleTask(11, 52, 0.00139, () => { Debug.WriteLine("task2: " + DateTime.Now); //here write the code that you want to schedule });
我的debugging窗口:
task2: 07.06.2017 11:52:00 task1: 07.06.2017 11:52:00 task2: 07.06.2017 11:52:05 task2: 07.06.2017 11:52:10 task1: 07.06.2017 11:52:15 task2: 07.06.2017 11:52:15 task2: 07.06.2017 11:52:20 task2: 07.06.2017 11:52:25 ...
只需将此类添加到您的项目中:
public class TaskScheduler { private static TaskScheduler _instance; private List<Timer> timers = new List<Timer>(); private TaskScheduler() { } public static TaskScheduler Instance => _instance ?? (_instance = new TaskScheduler()); public void ScheduleTask(int hour, int min, double intervalInHour, Action task) { DateTime now = DateTime.Now; DateTime firstRun = new DateTime(now.Year, now.Month, now.Day, hour, min, 0, 0); if (now > firstRun) { firstRun = firstRun.AddDays(1); } TimeSpan timeToGo = firstRun - now; if (timeToGo <= TimeSpan.Zero) { timeToGo = TimeSpan.Zero; } var timer = new Timer(x => { task.Invoke(); }, null, timeToGo, TimeSpan.FromHours(intervalInHour)); timers.Add(timer); } }
如果您想要运行可执行文件,请使用Windows计划任务。 我将假设(也许是错误的),你想要一个方法来运行你的当前程序。
为什么不直接运行一个线程来存储方法被调用的最后date呢?
是否每分钟唤醒一次(例如),如果当前时间大于指定时间,并且存储的最后date不是当前date,则调用该方法,然后更新date。
我刚刚写了ac#应用程序,每天必须重新启动。 我意识到这个问题是古老的,但我不认为它伤害增加另一个可能的解决scheme。 这是我在特定时间处理每日重启的方式。
public void RestartApp() { AppRestart = AppRestart.AddHours(5); AppRestart = AppRestart.AddMinutes(30); DateTime current = DateTime.Now; if (current > AppRestart) { AppRestart = AppRestart.AddDays(1); } TimeSpan UntilRestart = AppRestart - current; int MSUntilRestart = Convert.ToInt32(UntilRestart.TotalMilliseconds); tmrRestart.Interval = MSUntilRestart; tmrRestart.Elapsed += tmrRestart_Elapsed; tmrRestart.Start(); }
为了确保您的计时器保持在范围内,我build议使用System.Timers.Timer tmrRestart = new System.Timers.Timer()
方法在方法外创build它。 将方法RestartApp()
放在表单加载事件中。 当应用程序启动时,它将为AppRestart
设置值,如果current
时间大于重新启动时间,我们将1天添加到AppRestart
以确保重新启动按时发生,并且我们没有得到一个例外的负值进入定时器。 在tmrRestart_Elapsed
事件中,运行您需要的任何代码在特定时间运行。 如果你的应用程序自己重新启动,你不一定要停止计时器,但它也不会伤害,如果应用程序不重新启动,只需再次调用RestartApp()
方法,你会很好。
而不是设置一个时间来运行每60分钟的每秒,你可以计算剩余的时间,并设置计时器的一半(或其他一部分)。 通过这种方式,您不会检查时间,也可以通过计时器时间间隔确定一定的时间间隔,从而缩短您接近目标时间的时间。
例如,如果你想从现在起60分钟做一些事情,那么定时器的间隔将会是随机的:
30:00:00,15:00:00,07:30:00,03:45:00,00:00:01,RUN!
我使用下面的代码每天自动重新启动一次服务。 我使用了一个线程,因为我发现定时器在很长一段时间内是不可靠的,而在这个例子中它是唯一一个为此目的而创build的代价更高的代码,所以这个问题不重要。
(从VB.NET转换)
autoRestartThread = new System.Threading.Thread(autoRestartThreadRun); autoRestartThread.Start();
…
private void autoRestartThreadRun() { try { DateTime nextRestart = DateAndTime.Today.Add(CurrentSettings.AutoRestartTime); if (nextRestart < DateAndTime.Now) { nextRestart = nextRestart.AddDays(1); } while (true) { if (nextRestart < DateAndTime.Now) { LogInfo("Auto Restarting Service"); Process p = new Process(); p.StartInfo.FileName = "cmd.exe"; p.StartInfo.Arguments = string.Format("/C net stop {0} && net start {0}", "\"My Service Name\""); p.StartInfo.LoadUserProfile = false; p.StartInfo.UseShellExecute = false; p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; p.StartInfo.CreateNoWindow = true; p.Start(); } else { dynamic sleepMs = Convert.ToInt32(Math.Max(1000, nextRestart.Subtract(DateAndTime.Now).TotalMilliseconds / 2)); System.Threading.Thread.Sleep(sleepMs); } } } catch (ThreadAbortException taex) { } catch (Exception ex) { LogError(ex); } }
注意我已经设置了1000毫秒的最小间隔,这可以被增加,减less或删除,取决于你所需要的安全性。
记得在应用程序closures时也停止线程/计时器。
我有一个简单的方法来做到这一点。 这会在动作发生之前产生1分钟的延迟。 您可以添加秒,以使Thread.Sleep(); 短。
private void DoSomething(int aHour, int aMinute) { bool running = true; while (running) { Thread.Sleep(1); if (DateTime.Now.Hour == aHour && DateTime.Now.Minute == aMinute) { Thread.Sleep(60 * 1000); //Wait a minute to make the if-statement false //Do Stuff } } }
这可能只是我,但似乎这些答案中的大部分都不完整,或不能正常工作。 我做了一件非常快而肮脏的事情。 不知道这样做的想法有多好,但它每次都是完美的。
while (true) { if(DateTime.Now.ToString("HH:mm") == "22:00") { //do something here //ExecuteFunctionTask(); //Make sure it doesn't execute twice by pausing 61 seconds. So that the time is past 2200 to 2201 Thread.Sleep(61000); } Thread.Sleep(10000); }