C# 每小时执行一次方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19291816/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 14:39:27  来源:igfitidea点击:

Executing method every hour on the hour

c#timer

提问by RockOnGom

I want to execute a method every hour on the hour. I wrote some code,but it is not enough for my aim. Below code is working every 60 minutes.

我想每小时执行一个方法。我写了一些代码,但这对我的目标来说还不够。下面的代码每 60 分钟工作一次。

public void Start()
{
    System.Threading.Timer timerTemaUserBilgileri = new System.Threading.Timer(new System.Threading.TimerCallback(RunTakip), null, tmrTemaUserBilgileri, 0);
}

public void RunTakip(object temauserID)
{
    try 
    {
        string objID = "6143566557387";
        EssentialMethod(objID);
        TimeSpan span = DateTime.Now.Subtract(lastRunTime);
        if (span.Minutes > 60)
        {
            tmrTemaUserBilgileri = 1 * 1000;
            timerTemaUserBilgileri.Change(tmrTemaUserBilgileri, 0);
        }
        else
        {
            tmrTemaUserBilgileri = (60 - span.Minutes) * 60 * 1000;
            timerTemaUserBilgileri.Change(tmrTemaUserBilgileri, 0);
        }
        watch.Stop();
        var elapsedMs = watch.ElapsedMilliseconds;
    }
    catch (Exception ex)
    {
        timerTemaUserBilgileri.Change(30 * 60 * 1000, 0);
        Utils.LogYaz(ex.Message.ToString());
    }
}

public void EssentialMethod(objec obj)
{
    //some code
    lastRunTime = DateTime.Now;
    //send lastruntime to sql 
}

采纳答案by Anarion

If you want your code to be executed every 60 minutes:

如果您希望每 60 分钟执行一次代码:

aTimer = new System.Timers.Timer(60 * 60 * 1000); //one hour in milliseconds
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
aTimer.Start();
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
    //Do the stuff you want to be done every hour;
}

if you want your code to be executed every hour (i.e. 1:00, 2:00, 3:00) you can create a timer with some small interval (let's say a second, depends on precision you need) and inside that timer event check if an hour has passed

如果您希望您的代码每小时执行一次(即 1:00、2:00、3:00),您可以创建一个具有一些小间隔的计时器(假设一秒,取决于您需要的精度)并在该计时器事件中检查一个小时是否已经过去

aTimer = new System.Timers.Timer(1000); //One second, (use less to add precision, use more to consume less processor time
int lastHour = DateTime.Now.Hour;
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
aTimer.Start();
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
    if(lastHour < DateTime.Now.Hour || (lastHour == 23 && DateTime.Now.Hour == 0))
     {
           lastHour = DateTime.Now.Hour;
           YourImportantMethod(); // Call The method with your important staff..
     }

}

回答by John Richard Salt

Use a Cron Job on the server to call a function at the specified interval

使用服务器上的 Cron Job 以指定的时间间隔调用函数

Heres a link http://www.thesitewizard.com/general/set-cron-job.shtml

这是一个链接 http://www.thesitewizard.com/general/set-cron-job.shtml

回答by Paul Hutchinson

Just a small comment based on /Anarion's solution that I couldn't fit into a comment.

只是基于 /Anarion 的解决方案的一个小评论,我无法发表评论。

you can create a timer with some small interval (let's say a second, depends on precision you need)

您可以创建一个间隔很小的计时器(假设一秒钟,取决于您需要的精度)

You don't need it to go with any precision at all, you're thinking "how do I check this hour is the hour I want to fire". You could alternatively think "How do I check the next hour is the hour I want to fire" - once you think like that you realise you don't need any precision at all, just tick once an hour, and set a thread for the next hour. If you tick once an hour you know you'll be at some point before the next hour.

你根本不需要它的精确度,你在想“我如何检查这个时间是我想要开火的时间”。您也可以考虑“我如何检查下一个小时是我要开火的小时”-一旦您这样想,您就会意识到根本不需要任何精度,只需每小时打勾一次,然后为下一个小时。如果您每小时打勾一次,您就知道您将在下一小时之前的某个时间点。

Dim dueTime As New DateTime(Date.Today.Year, Date.Today.Month, Date.Today.Day, DateTime.Now.Hour + 1, 0, 0)
Dim timeRemaining As TimeSpan = dueTime.Subtract(DateTime.Now)

t = New System.Threading.Timer(New System.Threading.TimerCallback(AddressOf Method), Nothing, CType(timeRemaining.TotalMilliseconds, Integer), System.Threading.Timeout.Infinite)

回答by EscapeArtist

I agree with Se?or Salt that the chron job should be the first choice. However, the OP asked for every hour on the hour from c#. To do that, I set up the first timed event to fire on the hour:

我同意 Se?or Salt 的观点,即 chron 工作应该是首选。但是,OP 从 c# 每小时要求每小时一次。为此,我将第一个定时事件设置为在整点触发:

int MilliSecondsLeftTilTheHour()
{
    int interval;

    int minutesRemaining = 59 - DateTime.Now.Minute;
    int secondsRemaining = 59 - DateTime.Now.Second;
    interval = ((minutesRemaining * 60) + secondsRemaining) * 1000;

    // If we happen to be exactly on the hour...
    if (interval == 0)
    {
        interval = 60 * 60 * 1000;
    }
    return interval;
}

Timer timer = new Timer();
timer.Tick += timer_Tick;
timer.Enabled = true;
timer.Interval = MilliSecondsLeftTilTheHour();

The problem now is that if the above timer.Interval happens to be 45 minutes and 32 seconds, then the timer will continue firing every 45:32 not just the first time. So, inside the timer_Tick method, you have to readjust the timer.Interval to one hour.

现在的问题是,如果上面的 timer.Interval 恰好是 45 分 32 秒,那么计时器将每隔 45:32 继续触发,而不仅仅是第一次。因此,在 timer_Tick 方法中,您必须将 timer.Interval 重新调整为一小时。

 void timer_Tick(object sender, EventArgs e)
 {
     // The Interval could be hard wired here to  60 * 60 * 1000 but on clock 
     // resets and if the job ever goes longer than an hour, why not
     // recalculate once an hour to get back on track.
     timer.Interval = MilliSecondsLeftTilTheHour();
     DoYourThing();
 }

回答by Hasan A Yousef

What about trying the below code, the loop is determined to save your resources, and it is running every EXACT hour, i.e. with both minutes and seconds (and almost milliseconds equal to zero:

试试下面的代码怎么样,循环是为了节省你的资源,它每精确小时运行一次,即分钟和秒(几乎毫秒等于零:

using System;
using System.Threading.Tasks;

namespace COREserver{
    public static partial class COREtasks{   // partial to be able to split the same class in multiple files
        public static async void RunHourlyTasks(params Action[] tasks)
        {
            DateTime runHour = DateTime.Now.AddHours(1.0);
            TimeSpan ts = new TimeSpan(runHour.Hour, 0, 0);
            runHour = runHour.Date + ts;


            Console.WriteLine("next run will be at: {0} and current hour is: {1}", runHour, DateTime.Now);
            while (true)
            {
                TimeSpan duration = runHour.Subtract(DateTime.Now);
                if(duration.TotalMilliseconds <= 0.0)
                { 
                    Parallel.Invoke(tasks);
                    Console.WriteLine("It is the run time as shown before to be: {0} confirmed with system time, that is: {1}", runHour, DateTime.Now);
                    runHour = DateTime.Now.AddHours(1.0);
                    Console.WriteLine("next run will be at: {0} and current hour is: {1}", runHour, DateTime.Now);
                    continue;
                }
                int delay = (int)(duration.TotalMilliseconds / 2);
                await Task.Delay(30000);  // 30 seconds
            }
        }
    }
}

回答by user3235770

How about something simpler? Use a one-minute timer to check the hour:

更简单的东西怎么样?使用一分钟计时器检查小时:

public partial class Form1 : Form
{
    int hour;
    public Form1()
    {
        InitializeComponent();

        if(RunOnStartUp)
            hour = -1;
        else
            hour = DateTime.Now.Hour;

    }
    private void timer1_Tick(object sender, EventArgs e)
    {
        // once per minute:
        if(DateTime.Now.Hour != hour)
        {
            hour = DateTime.Now.Hour;
            DailyTask();
        }
    }
    private DailyTask()
    {
        // do something
    }
}

回答by Evander

Why is everyone trying to handle this problem with a timer?

为什么每个人都试图用计时器来处理这个问题?

you're doing two things... waiting until the top of the hour and then running your timer every hour on the hour.

你正在做两件事......等到小时,然后每小时运行你的计时器。

I have a windows service where I needed this same solution. I did my code in a very verbose way so that it is easy to follow for anyone. I know there are many shortcuts that can be implemented, but I leave that up to you.

我有一个 Windows 服务,我需要同样的解决方案。我以非常冗长的方式编写了代码,以便任何人都可以轻松理解。我知道有很多捷径可以实现,但我把它留给你。

private readonly Timer _timer;
/// starts timer
internal void Start()
{
    int waitTime = calculateSleepTime();

    System.Threading.Thread.Sleep(waitTime);

    object t = new object();

    EventArgs e = new EventArgs();

    CheckEvents(t, e);

    _timer.Start();
}

///  runs business logic everytime timer goes off
internal void CheckEvents(object sender, EventArgs e)
{
    //  do your logic here      
}

///  Calculates how long to wait until the top of the hour
private int calculateSleepTime()
{
    DateTime now = DateTime.Now;

    int minutes = now.Minute * 60 * 1000;

    int seconds = now.Second * 1000;

    int substrahend = now.Millisecond + seconds + minutes;

    int minuend = 60 * 60 * 1000;

    return minuend - substrahend;
}         

回答by Evander

Here's a simple, stable (self-synchronizing) solution:

这是一个简单、稳定(自同步)的解决方案:

while(true) {
    DoStuff();
    var now = DateTime.UtcNow;
    var previousTrigger = new DateTime(now.Year, now.Month, now.Day, now.Hour, 0, 0, now.Kind);
    var nextTrigger = previousTrigger + TimeSpan.FromHours(1);
    Thread.Sleep(nextTrigger - now);
}

Note that iterations may be skipped if DoStuff()takes longer than an hour to execute.

请注意,如果DoStuff()执行时间超过一个小时,则可能会跳过迭代。