C# 如何在.NET中每小时(或每小时的特定时间间隔)引发一个事件?
我正在开发一个小的网络爬虫程序,它会在系统托盘中运行,每小时都会抓取一个网站 让.NET每小时或每隔一段时间引发一个事件以执行某项任务的最佳方法是什么。例如,我希望根据时间每20分钟运行一次事件。活动将在以下地点举行:C# 如何在.NET中每小时(或每小时的特定时间间隔)引发一个事件?,c#,.net,scheduled-tasks,C#,.net,Scheduled Tasks,我正在开发一个小的网络爬虫程序,它会在系统托盘中运行,每小时都会抓取一个网站 让.NET每小时或每隔一段时间引发一个事件以执行某项任务的最佳方法是什么。例如,我希望根据时间每20分钟运行一次事件。活动将在以下地点举行: 00:20 00:40 01:00 01:20 01:40 等等。我能想到的最好的方法是在线程上创建一个循环,该循环不断地检查时间是否可以被给定的间隔整除,并在到达时间时引发回调事件。我觉得应该有更好的办法 我会使用计时器,但我更喜欢按照“时间表”按小时运行的东西,或者类似的东
00:20
00:40
01:00
01:20
01:40
等等。我能想到的最好的方法是在线程上创建一个循环,该循环不断地检查时间是否可以被给定的间隔整除,并在到达时间时引发回调事件。我觉得应该有更好的办法
我会使用计时器,但我更喜欢按照“时间表”按小时运行的东西,或者类似的东西
如果不在windows任务计划程序中设置我的应用程序,这是可能的吗
更新:
我正在添加计算计时器时间间隔的算法。此方法采用“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;
}
System.Windows.Forms.Timer(或System.Timers.Timer)
但既然您现在说不想使用计时器,那么您可以在另一个线程上运行轻量级等待进程(检查时间、睡眠几秒钟、再次检查时间……),或者制作一个组件,在特定的计划时间或间隔上引发事件(使用轻量级等待进程)。如果你想在一天中的特定时间跑步,你需要计算距离下一次跑步还有多长时间,并将其设置为你的间歇时间
这只是基本的想法。根据你需要的精确程度,你可以做得更多
int minutes = DateTime.Now.Minute;
int adjust = 10 - (minutes % 10);
timer.Interval = adjust * 60 * 1000;
您可以从Quartz.net中找到帮助,这里是一个使用线程计时和异步调用的轻量级系统的示例
我知道有一些缺点,但我喜欢在启动长时间运行的进程(如schedualed后端服务)时使用它而不是计时器。因为它在计时器线程中内联运行,所以您不必担心在原始调用完成之前它会再次启动。这可以扩展很多,以使它使用一个datetimes数组作为触发时间,或者为它添加更多的功能。我相信你们中的一些人知道一些更好的方法
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);
}
}
public Form1()
{
初始化组件();
//一些假数据,显然你会有自己的。
DateTime someStart=DateTime.Now.AddMinutes(1);
TimeSpan someInterval=TimeSpan.FromMinutes(2);
//抽样电话
StartTimer(一些开始,一些间隔,一些剂量);
}
//只是一个要调用的假函数
私人医院
{
DialogResult keepGoing=MessageBox.Show(“嘿,我做了什么!继续?”,“什么!”,MessageBoxButtons.YesNo);
返回(keepGoing==DialogResult.Yes);
}
//以下是实际的勇气。。并且可以移植到实际的类中。
私人代表无效无效功能(P1、P2、P3);
public void StartTimer(日期时间开始时间、时间间隔、函数操作)
{
voidFunc Timer=TimedThread;
Timer.BeginInvoke(开始时间、间隔、操作、null、null);
}
私有void TimedThread(DateTime startTime、TimeSpan interval、Func action)
{
bool-keepRunning=true;
DateTime NextExecute=startTime;
同时(继续修剪)
{
如果(DateTime.Now>NextExecute)
{
keepRunning=action.Invoke();
NextExecute=NextExecute.Add(间隔);
}
//可以参数化分辨率。
睡眠(1000);
}
}
另一种策略是记录进程上次运行的时间,并确定自该时间以来您所需的间隔是否已过。在该策略中,如果经过的时间等于或大于所需的时间间隔,则将事件编码为激发。通过这种方式,您可以处理如果计算机因某种原因停机,可能会错过较长时间间隔(例如,每天一次)的情况
例如:
- lastRunDateTime=5/2/2009晚上8点
- 我希望每24小时运行一次进程
- 在计时器事件中,检查自上次运行进程以来是否已超过24小时
- 如果是,则运行该进程,通过向其添加所需的时间间隔来更新lastRunDateTime(在本例中为24小时,但根据需要)
显然,为了在系统停机后恢复,您需要将lastRunDateTime存储在某个文件或数据库中,以便程序能够恢复到停止恢复的位置。我的目标是每晚3:00左右运行导入
以下是我的方法,使用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();
}
下面的内容应该可以做到这一点
static void Main(string[] Args)
{
try
{
MainAsync().GetAwaiter().GetResult();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
static async Task MainAsync()
{
CancellationTokenSource tokenSource = new CancellationTokenSource();
// Start the timed event here
StartAsync(tokenSource.Token);
Console.ReadKey();
tokenSource.Cancel();
tokenSource.Dispose();
}
public Task StartAsync(CancellationToken cancellationToken)
{
var nextRunTime = new DateTime();
switch (DateTime.Now.AddSeconds(1) < DateTime.Today.AddHours(12)) // add a second to current time to account for time needed to setup the task.
{
case true:
nextRunTime = DateTime.Today.AddHours(12); // Run at midday today.
break;
case false:
nextRunTime = DateTime.Today.AddDays(1).AddHours(12); // Run at midday tomorrow.
break;
}
var firstInterval = nextRunTime.Subtract(DateTime.Now);
Action action = () =>
{
// Run the task at the first interval, then run the task again at midday every day.
_timer = new Timer(
EventMethod,
null,
firstInterval,
DateTime.Today.AddDays(1).AddHours(12).Subtract(DateTime.Now)
);
};
// no need to await this call here because this task is scheduled to run later.
Task.Run(action);
return Task.CompletedTask;
}
private async void EventMethod(object state)
{
// do work
}
static void Main(字符串[]Args)
{
尝试
{
MainAsync().GetAwaiter().GetResult();
}
捕获(例外情况除外)
{
控制台写入线(例如消息);
}
}
静态异步任务mainsync()
{
CancellationTokenSource tokenSource=新的CancellationTokenSource();
//在此处启动定时事件
StartAsync(tokenSource.Token);
Console.ReadKey();
tokenSource.Cancel();
tokenSource.Dispose();
}
公共任务StartSync(CancellationToken CancellationToken)
{
var nextRunTime=new DateTime();
开关(DateTime.Now.AddSeconds(1)static void Main(string[] Args)
{
try
{
MainAsync().GetAwaiter().GetResult();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
static async Task MainAsync()
{
CancellationTokenSource tokenSource = new CancellationTokenSource();
// Start the timed event here
StartAsync(tokenSource.Token);
Console.ReadKey();
tokenSource.Cancel();
tokenSource.Dispose();
}
public Task StartAsync(CancellationToken cancellationToken)
{
var nextRunTime = new DateTime();
switch (DateTime.Now.AddSeconds(1) < DateTime.Today.AddHours(12)) // add a second to current time to account for time needed to setup the task.
{
case true:
nextRunTime = DateTime.Today.AddHours(12); // Run at midday today.
break;
case false:
nextRunTime = DateTime.Today.AddDays(1).AddHours(12); // Run at midday tomorrow.
break;
}
var firstInterval = nextRunTime.Subtract(DateTime.Now);
Action action = () =>
{
// Run the task at the first interval, then run the task again at midday every day.
_timer = new Timer(
EventMethod,
null,
firstInterval,
DateTime.Today.AddDays(1).AddHours(12).Subtract(DateTime.Now)
);
};
// no need to await this call here because this task is scheduled to run later.
Task.Run(action);
return Task.CompletedTask;
}
private async void EventMethod(object state)
{
// do work
}