Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何使用.NET定时器类在特定时间触发事件?_C#_.net_Timer - Fatal编程技术网

C# 如何使用.NET定时器类在特定时间触发事件?

C# 如何使用.NET定时器类在特定时间触发事件?,c#,.net,timer,C#,.net,Timer,我想在我的应用程序中触发一个事件,该事件在一天中的某个特定时间连续运行,比如下午4:00。我想每秒钟运行一次计时器,当时间等于下午4:00时运行事件。这很有效。但是我想知道是否有一种方法可以在下午4:00得到一次回调,而不必一直检查。这样的方法怎么样,使用类 请注意,如果使用System.Threading.Timer,则由TimerCallback指定的回调将在线程池(非UI)线程上执行,因此如果您计划在4:00对UI执行某些操作,则必须适当封送代码(例如,在Windows窗体应用程序中使用C

我想在我的应用程序中触发一个事件,该事件在一天中的某个特定时间连续运行,比如下午4:00。我想每秒钟运行一次计时器,当时间等于下午4:00时运行事件。这很有效。但是我想知道是否有一种方法可以在下午4:00得到一次回调,而不必一直检查。

这样的方法怎么样,使用类

请注意,如果使用
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运行的最简单方法有时。我在一些服务中使用这种方式进行日常管理。