2010-07-14 89 views
57

我在SO上搜索并找到关于Quartz.net的答案。但对我的项目来说似乎太大了。我想要一个等效的解决方案,但更简单和(最好)代码(不需要外部库)。我怎样才能每天在特定时间调用一种方法?如何在C#中的特定时间每天调用一个方法?

我需要补充一些有关此信息:

  • 做到这一点最简单的(丑陋的)方式,是检查的时间每一秒/分钟,调用该方法,在适当的时候

我想要一个更有效的方法来做到这一点,不需要经常检查时间,而且我可以控制工作是否完成了。如果方法失败(由于任何问题),程序应该知道写入日志/发送电子邮件。这就是为什么我需要调用方法,而不是安排工作。

我在Java中找到了这个解决方案Call a method at fixed time in Java。在C#中有类似的方法吗?

编辑:我已经这样做了。我在void Main()中添加了一个参数,并创建了一个bat(由Windows任务计划程序调度)以使用此参数运行程序。程序运行,完成工作,然后退出。如果一项工作失败,它可以写日志和发送电子邮件。这种方法适合我的要求以及:)

+2

该链接的问题似乎表明,您的运行应用程序中的方法必须定期调用。是这样吗?它会影响您是否需要进行中调度或是否只能使用Windows调度程序。 – paxdiablo 2010-07-14 04:21:57

+0

我的程序会根据需求连续运行 – Vimvq1987 2010-07-14 04:24:25

+0

嘿,我不敢相信你称我的答案“丑陋”。他们的斗争'的话:-) – paxdiablo 2010-07-14 04:39:13

回答

67
  • 创建一个控制台应用程序已经做了你在找什么
  • 使用Windows“Scheduled Tasks”功能,必须在你需要它运行时执行的控制台应用程序

这真是你所需要的!

更新:,如果你想这样做你的应用程序里面,你有几种选择:

    的Windows
  • 窗体的应用程序,你可以挖掘到Application.Idle事件和检查,看看是否你现在已经到了这个时候打电话给你的方法。此方法仅在您的应用程序不忙于其他内容时调用。一个快速检查,看看你的目标时间已经达到不应该把太多的压力,你的应用程序,我认为...
  • 在ASP.NET的Web应用程序,有“模拟”发送预定事件 - 看看这个CodeProject article
  • ,当然,你也可以只是简单的在任何.NET应用程序“滚你自己” - 看看这个CodeProject article对样品实施

更新#2:如果您想要每60分钟检查一次,您可以创建一个每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(); 
    } 
} 
+0

以前想过。 :)。但我的程序会持续运行,并且我想知道另一种方式,如果有:) – Vimvq1987 2010-07-14 04:23:02

+0

这是一个Winform应用程序。我会尽力说服老板改变设计,但起初我应该尽量满足他的要求:p – Vimvq1987 2010-07-14 04:25:35

+0

谢谢你的回答。我添加了一些信息。我很感激,如果你能帮助... – Vimvq1987 2010-07-14 04:37:27

4

,我知道的,也许最简单的就是使用Windows任务计划程序来执行最好的方法你在一天中的特定时间执行代码或让应用程序永久运行,并检查特定时间,或编写相同的Windows服务。

1

如果您想要运行可执行文件,请使用Windows计划任务。我将假设(也许是错误的)你想要一个方法在当前程序中运行。

为什么不只是有一个线程持续运行,存储方法被调用的最后日期?

是否每分钟唤醒一次(例如),如果当前时间大于指定时间并且存储的最后日期不是当前日期,则调用该方法,然后更新日期。

8

正如其他人所说的,您可以使用控制台应用程序在计划时运行。别人没有说的是,你可以通过这个应用程序触发你在主应用程序中等待的跨进程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(); 
    } 
} 
8

每当我建立需要这种功能的应用,我总是通过简单的.NET使用Windows任务计划程序图书馆,我发现。

请对see my answer to a similar question了解一些示例代码和更多解释。

4

我知道这是旧的,但这个怎么样:

建立一个定时器火在启动时计算时间,下次运行时间。在运行时的第一个调用中,取消第一个定时器并启动一个新的每日定时器。每天更改为小时或任何你想要的周期性。

+8

并且在夏时制时间更改期间观察它失败。 。 。 – 2013-03-14 20:49:00

+1

好点,虽然可以得到补偿。 – Brent 2014-03-12 14:14:56

0

您可以计算剩余时间并将计时器设置为这个的一半(或其他分数),而不是设置每60分钟每秒运行一次的时间。这样,您不会检查时间,而是可以在计时器间隔时间内确定一定程度的累计时间,从而缩短与目标时间的距离。

例如,如果你想要做一些事情,从现在60分钟计时器的时间间隔将aproximatly:

30:00:00,15:00:00,七时30分00秒,3时45分00秒,...,00:00:01,RUN!

我使用下面的代码每天自动重新启动一次服务。我使用了一个线程,因为我发现定时器在很长一段时间内不可靠,而在这个例子中这是比较昂贵的,它是唯一为此目的而创建的,所以这无关紧要。

(从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毫秒的间隔的最小值,这可以increaded,减少或去除,这取决于满足你的要求。

记得在应用程序关闭时也停止线程/计时器。

1

我刚刚写了一个不得不每天重新启动的c#应用程序。我意识到这个问题很老,但我不认为它会增加另一种可能的解决方案。这是我在特定时间处理每日重启的方式。

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(); 
} 

为了确保您的计时器保持在范围上,我建议使用System.Timers.Timer tmrRestart = new System.Timers.Timer()方法,该方法之外创建它。将方法RestartApp()放入您的表单加载事件中。应用程序启动时,将设置值AppRestart如果current比重启时间时,我们增加1天AppRestart,以确保重新启动发生在时间和我们没有得到一个例外放一个负值定时器。在tmrRestart_Elapsed事件中,运行您需要的任何代码在该特定时间运行。如果在它自己的应用程序重新启动你不一定要停止计时,但它也没有破坏,如果应用程序没有重新启动只需再次调用RestartApp()方法,你会好到哪里去。

-1

这可能只是我,但它似乎像大多数的答案都是不彻底或将无法正常工作。我做了一件非常快速和肮脏的事。这是说不知道这样做的想法是多么好,但它每次都是完美的。

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); 
} 
0

我对此有一个简单的方法。这会在动作发生之前创建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 
     } 
    } 
} 
2

我创建了一个易于使用的简单调度程序,不需要使用外部库。 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 
    }); 

我的调试窗口:

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); 
    } 
} 
0

这里有一个办法做到这一点使用TPL。无需创建/处理定时器等:

void ScheduleSomething() 
{ 

    var runAt = DateTime.Today + TimeSpan.FromHours(16); 

    if (runAt <= DateTime.Now) 
    { 
     DoSomething(); 
    } 
    else 
    { 
     var delay = runAt - DateTime.Now; 
     System.Threading.Tasks.Task.Delay(delay).ContinueWith(_ => DoSomething()); 
    } 

} 

void DoSomething() 
{ 
    // do somethig 
} 
相关问题