简体   繁体   English

计时器每5分钟运行一次

[英]Timer run every 5th minute

How can I run some function every 5th minute? 如何每隔5分钟运行一次功能? Example: I want run sendRequest() only at time 14:00, 14:05, 14:10 etc. 示例:我只想在时间14:00、14:05、14:10等运行sendRequest()

I would like to do it programmatically, in C#. 我想以编程方式在C#中做到这一点。 The application is a Windows service. 该应用程序是Windows服务。

Use System.Threading.Timer . 使用System.Threading.Timer You can specify a method to call periodically. 您可以指定一种定期调用的方法。

Example: 例:

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

You can use the second parameter to pass state to the callback. 您可以使用第二个参数将状态传递给回调。

Note that you'll need to keep your application alive somehow (eg, run it as a service). 请注意,您需要以某种方式使应用程序保持活动状态(例如,将其作为服务运行)。

As for how to make sure that it runs at hh:mm where mm % 5 == 0 , you can do the following. 至于如何确保它在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));

Note that the tolerance is necessary in case this code is executing when now is very close to the nearest hh:mm with mm % 5 == 0 . 请注意,如果now非常接近最接近的hh:mmmm % 5 == 0则此代码正在执行时, tolerance是必要的。 You can probably get away with a value smaller than one second but I'll leave that to you. 您可能会获得小于一秒的值,但是我会留给您。

The answer posted six years ago is useful. 六年前发布的答案很有用。 However, IMHO with modern C# it is now better to use the Task -based API with async and await . 但是,IMHO与现代C#最好现在将基于Task的API与asyncawait Also, I differ a little on the specifics of the implementation, such as how to manage the delay computation and how to round the current time to the next five minute interval. 另外,我在实现的细节上有些不同,例如如何管理延迟计算以及如何将当前时间四舍五入到下一个五分钟间隔。

First, let's assume the sendRequest() method returns void and has no parameters. 首先,假设sendRequest()方法返回void并且没有参数。 Then, let's assume that the basic requirement is to run it roughly every five minutes (ie it's not that important that it run exactly on five-minute divisions of the hour). 然后,让我们假设基本要求是大约每五分钟运行一次(即,按小时的五分钟间隔精确运行并不重要)。 Then that can be implemented very easily, like this: 然后可以很容易地实现,如下所示:

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

It can be called like this: 可以这样称呼:

CancellationTokenSource tokenSource = new CancellationTokenSource();

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

When tokenSource.Cancel() is called, the loop will be interrupted by the TaskCanceledException thrown at the await Task.Delay(...) statement. 调用tokenSource.Cancel() ,循环将被await Task.Delay(...)语句引发的TaskCanceledException中断。 Otherwise, the sendRequest() method will be called every five minutes (with it being called immediately when the RunPeriodically() method is called…you can reorder the statements in the loop if you want it to wait the first time too). 否则,将每五分钟调用一次sendRequest()方法(当调用RunPeriodically()方法时将立即调用该方法……如果您也希望它第一次等待,则可以在循环中对语句重新排序)。

That's the simplest version. 那是最简单的版本。 If instead you do want to perform the action exactly on five minute intervals, you can do something similar, but compute the next run time and delay for an appropriate amount of time. 相反,如果您确实希望每隔五分钟精确地执行一次操作,则可以执行类似的操作,但是要计算下一次运行时间和适当时间的延迟。 For example: 例如:

// 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;
    }
}

Called like this: 这样称呼:

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

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

Where the helper method RoundCurrentToNextFiveMinutes() looks like this: 辅助方法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);
}

In either example, the timerTask object can be used to monitor the state of the loop. 在任一示例中, timerTask对象均可用于监视循环状态。 In most cases, it's probably not needed. 在大多数情况下,可能不需要。 But if you want to be able to, eg await until some other part of the code cancels the loop, this object is what you'd use. 但是,如果您希望(例如) await代码的其他部分取消循环,则可以使用该对象。

Note that the Task.Delay() method does use a timer in its implementation. 请注意, Task.Delay()方法在其实现中确实使用了计时器。 The above is not suggested for the purpose of avoiding the use of a timer, but rather to show an implementation of the original goal using the modern C# async / await features. 上面的建议不是为了避免使用计时器,而是为了展示使用现代C# async / await功能实现原始目标的目的。 This version will mesh more cleanly with other code that is already using async / await . 这个版本将与已经使用async / await其他代码更加清晰地啮合。

Use System.Threading.Timer: 使用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
    }
}

This might look somewhat clumsy and inefficient since it has to call every second, but the actual number of CPU cycles that get used to perform the check once a second is totally negligible on virtually any hardware. 这看起来有些笨拙且效率低下,因为它必须每秒调用一次,但是实际上在任何硬件上,用于每秒执行一次检查的CPU周期的实际数量都可以忽略不计。

(Sorry if the code isn't 100% correct; I don't have an IDE on me right now. (很抱歉,如果代码不是100%正确;我现在没有IDE。

You can make a thread which include a loop like this 您可以创建一个包含这样的循环的线程

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

It's a bit quick and dirty but gonna do the job :) 这有点快速又肮脏,但是会做的:)

you could have a thread running that first 你可以先运行一个线程

  1. checks how long to sleep until next 5th minute (so if it's 13:59:51, it sleeps for 9 seconds) 检查直到下一个5分钟的睡眠时间(因此如果是13:59:51,它会睡9秒钟)
  2. does the action; 采取行动;
  3. then sleeps for 5 minutes 然后睡5分钟
  4. goes to step 2 转到步骤2

This class is everythign you need, you just setup the amount of time between delegate callings and your delegate and thats it :) 此类是您需要的一切,您只需设置委托调用与您的委托之间的时间,就可以了:)

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

You better give you own Thread that executes the method a condition 您最好给自己的线程执行条件

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

What you are looking for is code that implements 'cron' jobs. 您正在寻找的是实现“ cron”作业的代码。 There are a few libraries out there that do this (quartz.net is one of them). 有一些库可以做到这一点(quartz.net是其中之一)。 I found that many of them are bulky and have many features. 我发现其中许多体积庞大且具有许多功能。 Here is a leaner implementation that I have used in some projects: 这是我在某些项目中使用的更精简的实现:

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

PS My server appears to be down right now. PS我的服务器现在似乎已关闭。 I am working on that. 我正在努力。

Hope that helps. 希望能有所帮助。

Bob 鲍勃

There are a few ways to do this, you can create a timer that runs every 5 minutes and start it when the time reaches one of the 5 minute intervals, or have it run every few seconds and check if the time is divisible by 5 有几种方法可以执行此操作,您可以创建一个每5分钟运行一次的计时器,并在该时间达到5分钟间隔之一时启动它,或者让它每隔几秒运行一次,然后检查时间是否可以被5整除

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

then create a tick function and add an event handler 然后创建一个tick函数并添加一个事件处理程序

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

I agree with Peter Duniho - modern C# is far better at this stuff. 我同意彼得·杜尼奥(Peter Duniho)的观点-现代C#在这方面做得更好。

I would suggest using Microsoft's Reactive Framework (NuGet "System.Reactive") and then you can do this: 我建议使用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. */
    });

I get this kind of result: 我得到这样的结果:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM