定时器每隔5分钟运行一次

时间:2010-12-10 14:07:35

标签: c#

如何每隔5分钟运行一些功能?示例:我只想在14:00,14:05,14:10等时间运行sendRequest()

我想在C#中以编程方式执行此操作。该应用程序是Windows服务。

11 个答案:

答案 0 :(得分:19)

使用System.Threading.Timer。您可以指定定期调用的方法。

示例:

Timer timer = new Timer(Callback, null, TimeSpan.Zero, TimeSpan.FromMinutes(5));

public void Callback(object state) {
    Console.WriteLine("The current time is {0}", DateTime.Now);
}

您可以使用第二个参数将状态传递给回调。

请注意,您需要以某种方式保持应用程序活动(例如,将其作为服务运行)。

至于如何确保它在hh:mm mm % 5 == 0处运行,您可以执行以下操作。

DateTime now = DateTime.Now;
int additionalMinutes = 5 - now.Minute % 5;
if(additionalMinutes == 0) {
    additionalMinutes = 5;
}
var nearestOnFiveMinutes = new DateTime(
    now.Year,
    now.Month,
    now.Day,
    now.Hour,
    now.Minute,
    0
).AddMinutes(additionalMinutes);
TimeSpan timeToStart = nearestOnFiveMinutes.Subtract(now);
TimeSpan tolerance = TimeSpan.FromSeconds(1);
if (timeToStart < tolerance) {
    timeToStart = TimeSpan.Zero;
}

var Timer = new Timer(callback, null, timeToStart, TimeSpan.FromMinutes(5));

请注意,如果tolerancenow非常接近hh:mm时执行此代码,则mm % 5 == 0是必需的。你可能会得到一个小于一秒的值,但我会把它留给你。

答案 1 :(得分:11)

The answer posted six years ago很有用。但是,使用现代C#的恕我直言,现在最好使用基于Task的API asyncawait。另外,我对实现的具体细节略有不同,例如如何管理延迟计算以及如何将当前时间舍入到下一个五分钟的间隔。

首先,让我们假设sendRequest()方法返回void并且没有参数。然后,让我们假设基本要求是每五分钟大致运行它(即,它在一小时的五分钟内完全运行并不重要)。然后就可以很容易地实现,如下所示:

async Task RunPeriodically(Action action, TimeSpan interval, CancellationToken token)
{
    while (true)
    {
        action();
        await Task.Delay(interval, token);
    }
}

可以像这样调用:

CancellationTokenSource tokenSource = new CancellationTokenSource();

Task timerTask = RunPeriodically(sendRequest, TimeSpan.FromMinutes(5), tokenSource.Token);

调用tokenSource.Cancel()时,循环将被TaskCanceledException语句中抛出的await Task.Delay(...)中断。否则,sendRequest()方法将每五分钟调用一次(在调用RunPeriodically()方法时立即调用它...如果您希望它在第一次等待,您可以重新排序循环中的语句)。

这是最简单的版本。相反,如果您确实希望以五分钟的间隔执行操作,则可以执行类似的操作,但计算下一个运行时间并延迟一段适当的时间。例如:

// private field somewhere appropriate; it would probably be best to put
// this logic into a reusable class.
DateTime _nextRunTime;

async Task RunPeriodically(Action action,
    DateTime startTime, TimeSpan interval, CancellationToken token)
{
    _nextRunTime = startTime;

    while (true)
    {
        TimeSpan delay = _nextRunTime - DateTime.UtcNow;

        if (delay > TimeSpan.Zero)
        {                
            await Task.Delay(delay, token);
        }

        action();
        _nextRunTime += interval;
    }
}

这样称呼:

CancellationTokenSource tokenSource = new CancellationTokenSource();
DateTime startTime = RoundCurrentToNextFiveMinutes();

Task timerTask = RunPeriodically(sendRequest,
    startTime, TimeSpan.FromMinutes(5), tokenSource.Token);

辅助方法RoundCurrentToNextFiveMinutes()如下所示:

DateTime RoundCurrentToNextFiveMinutes()
{
    DateTime now = DateTime.UtcNow,
        result = new DateTime(now.Year, now.Month, now.Day, now.Hour, 0, 0);

    return result.AddMinutes(((now.Minute / 5) + 1) * 5);
}

在任一示例中,timerTask对象都可用于监视循环的状态。在大多数情况下,可能不需要它。但是,如果你想能够,例如, await直到代码的其他部分取消循环,这个对象就是你要使用的。

请注意,Task.Delay()方法在其实现中确实使用了计时器。上述内容不建议用于避免使用计时器,而是使用现代C#async / await功能显示原始目标的实现。此版本将与已使用async / await的其他代码更清晰地进行网格划分。

答案 2 :(得分:9)

使用System.Threading.Timer:

var timer = new Timer(TimerTick, null, TimeSpan.Zero, new TimeSpan(0, 0, 0, 1));

int lastMinute = 1;

void TimerTick(object state)
{
    var minute = DateTime.Now.Minutes;
    if (minute != lastMinute && minute % 5 == 0)
    {
        lastMinute = minute;
        //do stuff
    }
}

这可能看起来有点笨拙和低效,因为它必须每秒调用一次,但实际上每秒执行一次检查的CPU周期数实际上几乎可以忽略不计。

(对不起,如果代码不是100%正确;我现在没有IDE。

答案 3 :(得分:5)

你可以创建一个包含这样的循环的线程

void Runner(){
   while(1){
         Thread t = new thread( target_method );
         t.start();
         sleep(5 * 60 * 1000);
   }
}

它有点快速和肮脏,但要完成这项工作:)

答案 4 :(得分:3)

你可以先运行一个线程

  1. 检查下一个5分钟的睡眠时间(如果它是13:59:51,它会睡9秒钟)
  2. 行动;
  3. 然后睡了5分钟
  4. 转到第2步

答案 5 :(得分:3)

这个课程就是你需要的一切,你只需要设定代表电话和委托之间的时间,就是这样:)

http://msdn.microsoft.com/en-us/library/system.threading.timer.aspx

答案 6 :(得分:3)

你最好给自己一个执行条件的线程

While(programStatus == ProgramStatus.Working)
{
   While (true)
   {
      // Do your stuff
   }
   Thread.Sleep(TheTimeYouWantTheThreadToWaitTillTheNextOperation);
}

答案 7 :(得分:2)

您正在寻找的是实现“cron”工作的代码。有一些库可以做到这一点(quartz.net就是其中之一)。我发现它们中的许多都很笨重并且有许多功能。这是我在一些项目中使用的更精简的实现:

http://blog.bobcravens.com/2009/10/an-event-based-cron-scheduled-job-in-c/

P.S。我的服务器现在似乎已关闭。我正在努力。

希望有所帮助。

鲍勃

答案 8 :(得分:2)

有几种方法可以做到这一点,你可以创建一个每5分钟运行一次的计时器,当时间达到5分钟间隔之一时启动它,或者让它每隔几秒运行一次并检查时间是否可以分割按5

System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer(); // create a new timer
timer.interval = 300000; //300000 = 5 minutes

然后创建一个tick函数并添加一个事件处理程序

timer.Tick += new EventHandler(TimerTickHandler); //add the event handler
timer.Start(); //start the timer

答案 9 :(得分:1)

我同意Peter Duniho的观点 - 现代C#在这方面要好得多。

我建议使用Microsoft的Reactive Framework(NuGet“System.Reactive”)然后你可以这样做:

IObservable<long> timer =
    Observable
        .Defer(() =>
        {
            var now = DateTimeOffset.Now;
            var result = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, 0, 0, now.Offset);
            result = result.AddMinutes(((now.Minute / 5) + 1) * 5);
            return Observable.Timer(result, TimeSpan.FromMinutes(5.0));
        });

    timer.Subscribe(x => 
    {
        /* Your code here that run on the 5 minute every 5 minutes. */
    });

我得到了这样的结果:

2016/08/11 14:40:00 +09:30
2016/08/11 14:45:00 +09:30
2016/08/11 14:50:00 +09:30

答案 10 :(得分:0)