C# 如何使用.NET定时器类在特定时间触发事件?
我想在我的应用程序中触发一个事件,该事件在一天中的某个特定时间连续运行,比如下午4:00。我想每秒钟运行一次计时器,当时间等于下午4:00时运行事件。这很有效。但是我想知道是否有一种方法可以在下午4:00得到一次回调,而不必一直检查。这样的方法怎么样,使用类 请注意,如果使用C# 如何使用.NET定时器类在特定时间触发事件?,c#,.net,timer,C#,.net,Timer,我想在我的应用程序中触发一个事件,该事件在一天中的某个特定时间连续运行,比如下午4:00。我想每秒钟运行一次计时器,当时间等于下午4:00时运行事件。这很有效。但是我想知道是否有一种方法可以在下午4:00得到一次回调,而不必一直检查。这样的方法怎么样,使用类 请注意,如果使用System.Threading.Timer,则由TimerCallback指定的回调将在线程池(非UI)线程上执行,因此如果您计划在4:00对UI执行某些操作,则必须适当封送代码(例如,在Windows窗体应用程序中使用C
System.Threading.Timer
,则由TimerCallback
指定的回调将在线程池(非UI)线程上执行,因此如果您计划在4:00对UI执行某些操作,则必须适当封送代码(例如,在Windows窗体应用程序中使用Control.Invoke
,或在WPF应用程序中使用Dispatcher.Invoke
。有关详细信息,请使用See
如果您想自己编写,也可以使用下面的代码:
public void InitTimer()
{
DateTime time = DateTime.Now;
int second = time.Second;
int minute = time.Minute;
if (second != 0)
{
minute = minute > 0 ? minute-- : 59;
}
if (minute == 0 && second == 0)
{
// DoAction: in this function also set your timer interval to 24 hours
}
else
{
TimeSpan span = //new daily timespan, previous code was hourly: new TimeSpan(0, 60 - minute, 60 - second);
timer.Interval = (int) span.TotalMilliseconds - 100;
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
}
void timer_Tick(object sender, EventArgs e)
{
timer.Interval = ...; // 24 hours
// DoAction
}
任务调度器是一个更好的选择,它可以很容易地在C#中使用。从.NET 4.5开始,有一个非常干净的解决方案:
public async void ScheduleAction(Action action, DateTime ExecutionTime)
{
await Task.Delay((int)ExecutionTime.Subtract(DateTime.Now).TotalMilliseconds);
action();
}
public class DailyTrigger
{
readonly TimeSpan triggerHour;
public DailyTrigger(int hour, int minute = 0, int second = 0)
{
triggerHour = new TimeSpan(hour, minute, second);
InitiateAsync();
}
async void InitiateAsync()
{
while (true)
{
var triggerTime = DateTime.Today + triggerHour - DateTime.Now;
if (triggerTime < TimeSpan.Zero)
triggerTime = triggerTime.Add(new TimeSpan(24,0,0));
await Task.Delay(triggerTime);
OnTimeTriggered?.Invoke();
}
}
public event Action OnTimeTriggered;
}
以下是一个不使用异步/等待的解决方案:
public void Execute(Action action, DateTime ExecutionTime)
{
Task WaitTask = Task.Delay((int)ExecutionTime.Subtract(DateTime.Now).TotalMilliseconds);
WaitTask.ContinueWith(_ => action);
WaitTask.Start();
}
需要注意的是,由于int32 max value的缘故,这只在24天内有效,这对于您的案例来说已经足够了,但值得注意。我这样做是为了每天早上7点点火
bool _ran = false; //initial setting at start up
private void timer_Tick(object sender, EventArgs e)
{
if (DateTime.Now.Hour == 7 && _ran==false)
{
_ran = true;
Do_Something();
}
if(DateTime.Now.Hour != 7 && _ran == true)
{
_ran = false;
}
}
回应Dan的解决方案,使用Timercallback是一个快速而简洁的解决方案。 在要计划运行任务或子例程的方法中,使用以下命令:
t = New Timer(Sub()
'method call or code here'
End Sub, Nothing, 400, Timeout.Infinite)
使用“Timeout.Infinite”将确保在400ms后只执行一次回调。我使用的是VB.Net。以VoteCoffes为例,下面是一个基于事件的紧凑解决方案:
public async void ScheduleAction(Action action, DateTime ExecutionTime)
{
await Task.Delay((int)ExecutionTime.Subtract(DateTime.Now).TotalMilliseconds);
action();
}
public class DailyTrigger
{
readonly TimeSpan triggerHour;
public DailyTrigger(int hour, int minute = 0, int second = 0)
{
triggerHour = new TimeSpan(hour, minute, second);
InitiateAsync();
}
async void InitiateAsync()
{
while (true)
{
var triggerTime = DateTime.Today + triggerHour - DateTime.Now;
if (triggerTime < TimeSpan.Zero)
triggerTime = triggerTime.Add(new TimeSpan(24,0,0));
await Task.Delay(triggerTime);
OnTimeTriggered?.Invoke();
}
}
public event Action OnTimeTriggered;
}
这个解决方案怎么样
Sub Main()
Dim t As New Thread(AddressOf myTask)
t.Start()
Console.ReadLine()
End Sub
Private Sub myTask()
Dim a = "14:35"
Dim format = "dd/MM/yyyy HH:mm:ss"
Dim targetTime = DateTime.Parse(a)
Dim currentTime = DateTime.Parse(Now.ToString(format))
Console.WriteLine(currentTime)
Console.WriteLine("target time " & targetTime)
Dim bb As TimeSpan = targetTime - currentTime
If bb.TotalMilliseconds < 0 Then
targetTime = targetTime.AddDays(1)
bb = targetTime - currentTime
End If
Console.WriteLine("Going to sleep at " & Now.ToString & " for " & bb.TotalMilliseconds)
Thread.Sleep(bb.TotalMilliseconds)
Console.WriteLine("Woke up at " & Now.ToString(format))
End Sub
Sub-Main()
Dim t作为新线程(myTask的地址)
t、 开始()
Console.ReadLine()
端接头
私有子任务myTask()
Dim a=“14:35”
Dim format=“dd/MM/yyyy HH:MM:ss”
Dim targetTime=DateTime.Parse(a)
Dim currentTime=DateTime.Parse(Now.ToString(格式))
Console.WriteLine(当前时间)
Console.WriteLine(“目标时间”和目标时间)
Dim bb As TimeSpan=目标时间-当前时间
如果bb.totalms<0,则
targetTime=targetTime.AddDays(1)
bb=目标时间-当前时间
如果结束
Console.WriteLine(“将在“&Now.ToString&”for“&bb.total毫秒”处睡眠)
线程睡眠(bb.total毫秒)
Console.WriteLine(“在”&Now.ToString(格式))醒来)
端接头
net有很多定时器类,但是它们相对于当前时间都需要时间跨度。在相对的时间内,在启动定时器并监视定时器运行时,有很多事情要考虑。
- 如果计算机进入待机状态怎么办
- 如果计算机的时间改变怎么办
- 如果到期时间是在夏令时转换之后呢
- 如果用户在启动计时器后更改了计算机的时区,从而在过期之前出现了以前没有的新时区转换,该怎么办
操作系统非常适合处理这种复杂性,而运行在.NET上的应用程序代码则不然
对于Windows,NuGet软件包包装了一个在绝对时间过期的操作系统计时器。听起来不错,但有一个问题。t.Change(timeUntilFour,Timeout.Infinite);
给出一个错误,表示timespan、timespan没有过载。我在文档中也可以调用dispose,但不确定在哪里。有什么想法吗?这有效:t.Change(timeUntilFour,new timespan(Timeout.Infinite));在休眠、待机等情况下,该功能是否正常工作?此代码假设从午夜到下午4:00有16小时。该假设在夏令时转换期间无效。该代码还假设系统时钟始终准确。如果出现错误,需要调整,则该代码不会检测到该错误并进行调整它的剩余时间相应地在t
上。@Øyvind Brå然后,我写了这篇每小时一次的文章(之前),你可以在评论中看到它,你可以使用DateTime.Today.AddHours(16)对于16,但如果DateTime,您也可以使用它。现在是您的时间间隔中的16:00,将每小时调用一次,而且我的主要重点是使用windows任务计划程序,而不是创建weal。(我不喜欢编辑代码,我将它作为家庭作业:D,Dan代码与此类似)实际上,您现在可以对任务使用时间跨度。延迟,意味着您可以超过VoteCoffee提到的24天限制。时间跨度基本上没有限制,大约1000万天,很好的解决方案,但“执行时间”必须在未来或任务中。延迟将抛出ArgumentOutOfRangeException。请注意不要使用async void
,action()
中的异常将导致整个应用程序崩溃。阅读更多信息:@chris84948,即使您给它一个时间跨度,Task.Delay(Date.Subtract(DateTime.Now))
当超过int32.MaxValue时,它将抛出aurgumentoutfrange。这对我来说似乎有点危险。我认为您应该添加一个检查,确保计算出的毫秒数为正数。特别是因为它将永远不会完成,并且会抛出其他负值。从某种意义上说,这是让某些东西每天以c运行的最简单方法有时。我在一些服务中使用这种方式进行日常管理。