C# 如何在 .NET 中每小时(或每小时的特定时间间隔)引发一个事件?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/307798/
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
How can I raise an event every hour (or specific time interval each hour) in .NET?
提问by Dan Herbert
I'm working on a little web crawler that will run in the system tray and crawl a web site every hour on the hour.
我正在开发一个小网络爬虫,它将在系统托盘中运行,每小时每小时爬一次网站。
What is the best way to get .NET to raise an event every hour or some other interval to perform some task. For example I want to run an event every 20 minutes based on the time. The event would be raised at:
让 .NET 每小时或其他时间间隔引发事件以执行某些任务的最佳方法是什么。例如,我想根据时间每 20 分钟运行一次事件。该事件将在:
00:20
00:40
01:00
01:20
01:40
and so on. The best way I can think of to do this is by creating a loop on a thread, that constantly checks if the time is divisible by a given interval and raises a callback event if the time is reached. I feel like there has got to be a better way.
等等。我能想到的最好的方法是在线程上创建一个循环,它不断检查时间是否可以被给定的间隔整除,并在到达时间时引发回调事件。我觉得必须有更好的方法。
I'd use a Timer
but I'd prefer something that follows a "schedule" that runs on the hour or something along those lines.
我会使用 aTimer
但我更喜欢遵循按小时运行的“时间表”或沿着这些路线运行的东西。
Without setting up my application in the windows task scheduler is this possible?
如果没有在 Windows 任务调度程序中设置我的应用程序,这可能吗?
UPDATE:
I'm adding my algorithm for calculating the time interval for a timer. This method takes a "minute
" parameter, which is what time the timer should trigger a tick. For example, if the "minute
" parameter is 20, then the timer will tick at the intervals in the timetable above.
更新:
我正在添加我的算法来计算计时器的时间间隔。这个方法需要一个“ minute
”参数,它是计时器应该触发滴答的时间。例如,如果“ minute
”参数为 20,则计时器将按上述时间表中的时间间隔计时。
int CalculateTimerInterval(int minute)
{
if (minute <= 0)
minute = 60;
DateTime now = DateTime.Now;
DateTime future = now.AddMinutes((minute - (now.Minute % minute))).AddSeconds(now.Second * -1).AddMilliseconds(now.Millisecond * -1);
TimeSpan interval = future - now;
return (int)interval.TotalMilliseconds;
}
This code is used as follows:
这段代码的用法如下:
static System.Windows.Forms.Timer t;
const int CHECK_INTERVAL = 20;
static void Main()
{
t = new System.Windows.Forms.Timer();
t.Interval = CalculateTimerInterval(CHECK_INTERVAL);
t.Tick += new EventHandler(t_Tick);
t.Start();
}
static void t_Tick(object sender, EventArgs e)
{
t.Interval = CalculateTimerInterval(CHECK_INTERVAL);
}
采纳答案by tvanfosson
System.Timers.Timer. If you want to run at specific times of the day, you will need to figure out how long it is until the next time and set that as your interval.
系统.定时器.定时器。如果您想在一天中的特定时间运行,则需要计算距离下一次的时间并将其设置为您的间隔时间。
This is just the basic idea. Depending on how precise you need to be you can do more.
这只是基本的想法。根据您需要达到的精确程度,您可以做得更多。
int minutes = DateTime.Now.Minute;
int adjust = 10 - (minutes % 10);
timer.Interval = adjust * 60 * 1000;
回答by Steven A. Lowe
System.Windows.Forms.Timer (or System.Timers.Timer)
System.Windows.Forms.Timer(或 System.Timers.Timer)
but since now you say you don't want to use Timers, you can run a lightweight wait process on another thread (check time, sleep a few seconds, check time again...) or make a component that raises an event (using a lightweight wait process) on certain scheduled times or intervals
但是既然现在你说你不想使用定时器,你可以在另一个线程上运行一个轻量级的等待过程(检查时间,休眠几秒钟,再次检查时间......)或制作一个引发事件的组件(使用一个轻量级的等待过程)在某些预定的时间或间隔
回答by suhair
You may find help from Quartz.net http://quartznet.sourceforge.net/
您可以从 Quartz.net http://quartznet.sourceforge.net/找到帮助
回答by ExCodeCowboy
Here is an example of a lightweight system using thread timing and an asynch call.
这是一个使用线程计时和异步调用的轻量级系统示例。
I know there are some downsides, but I like using this instead of a timer when kicking off a long running process (like schedualed backend services). Since it runs inline in the timer thread, you don't have to worry about it getting kicked off again before the the original call has finished. This could be extended quite a bit to make it use an array of datetimes as the trigger times or add some more abilities to it. I am sure some of you guys out there know some better ways.
我知道有一些缺点,但我喜欢在启动长时间运行的进程(如调度的后端服务)时使用它而不是计时器。由于它在计时器线程中内联运行,因此您不必担心它会在原始调用完成之前再次启动。这可以扩展很多以使其使用日期时间数组作为触发时间或为其添加更多功能。我相信你们中的一些人知道一些更好的方法。
public Form1()
{
InitializeComponent();
//some fake data, obviously you would have your own.
DateTime someStart = DateTime.Now.AddMinutes(1);
TimeSpan someInterval = TimeSpan.FromMinutes(2);
//sample call
StartTimer(someStart,someInterval,doSomething);
}
//just a fake function to call
private bool doSomething()
{
DialogResult keepGoing = MessageBox.Show("Hey, I did something! Keep Going?","Something!",MessageBoxButtons.YesNo);
return (keepGoing == DialogResult.Yes);
}
//The following is the actual guts.. and can be transplanted to an actual class.
private delegate void voidFunc<P1,P2,P3>(P1 p1,P2 p2,P3 p3);
public void StartTimer(DateTime startTime, TimeSpan interval, Func<bool> action)
{
voidFunc<DateTime,TimeSpan,Func<bool>> Timer = TimedThread;
Timer.BeginInvoke(startTime,interval,action,null,null);
}
private void TimedThread(DateTime startTime, TimeSpan interval, Func<bool> action)
{
bool keepRunning = true;
DateTime NextExecute = startTime;
while(keepRunning)
{
if (DateTime.Now > NextExecute)
{
keepRunning = action.Invoke();
NextExecute = NextExecute.Add(interval);
}
//could parameterize resolution.
Thread.Sleep(1000);
}
}
回答by Casey Gay
Another strategy for this would be to record the LAST TIME that the process was run and determine if your desired interval has elapsed since that time. In this strategy, you would code your event to fire if the elapsed time is equal to OR GREATER THAN the desired interval. In this way you can handle instances where long intervals (once per day, for example) could be missed if the computer were to be down for some reason.
另一个策略是记录该进程运行的最后一次,并确定自该时间以来是否已经过去了所需的时间间隔。在此策略中,如果经过的时间等于或大于所需的时间间隔,您将编码您的事件以触发。通过这种方式,您可以处理如果计算机由于某种原因停机而可能会错过很长的时间间隔(例如,每天一次)的情况。
So for example:
例如:
- lastRunDateTime = 5/2/2009 at 8pm
- I want to run my process every 24 hours
- On a timer event, check whether 24 hours OR MORE passed since the last time the process was run.
- If yes, run the process, update lastRunDateTime by adding the desired interval to it (24 hours in this case, but whatever you need it to be)
- lastRunDateTime = 2009 年 5 月 2 日晚上 8 点
- 我想每 24 小时运行一次我的进程
- 在计时器事件上,检查自上次运行该进程以来是否过去了 24 小时或更长时间。
- 如果是,请运行该过程,通过向它添加所需的时间间隔来更新 lastRunDateTime(在这种情况下为 24 小时,但无论您需要它是什么)
Obviously, for this to recover after the system has gone down, you will need to store lastRunDateTime in a file or database somewhere so the program could pick up where it left off on recovery.
显然,为了在系统宕机后恢复,您需要将 lastRunDateTime 存储在文件或数据库中的某个位置,以便程序可以在恢复时从中断的地方继续。
回答by hey
My goal is to run an import around 03:00 every night.
我的目标是每晚 03:00 左右运行导入。
Here's my approach, using System.Timers.Timer:
这是我的方法,使用 System.Timers.Timer:
private Timer _timer;
private Int32 _hours = 0;
private Int32 _runAt = 3;
protected override void OnStart(string[] args)
{
_hours = (24 - (DateTime.Now.Hour + 1)) + _runAt;
_timer = new Timer();
_timer.Interval = _hours * 60 * 60 * 1000;
_timer.Elapsed += new ElapsedEventHandler(Tick);
_timer.Start();
}
void Tick(object sender, ElapsedEventArgs e)
{
if (_hours != 24)
{
_hours = 24;
_timer.Interval = _hours * 60 * 60 * 1000;
}
RunImport();
}