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

时间:2010-12-25 01:51:37

标签: c# .net timer

我希望在我的应用程序中触发一个事件,该事件在某一时间持续运行,例如在下午4:00。我想过每秒运行一次计时器,当时间等于下午4点时,运行该事件。这样可行。但是我想知道是否有办法在下午4点获得回调,而不必继续检查。

9 个答案:

答案 0 :(得分:42)

使用System.Threading.Timer类?

这样的事情怎么样?
var t = new Timer(TimerCallback);

// Figure how much time until 4:00
DateTime now = DateTime.Now;
DateTime fourOClock = DateTime.Today.AddHours(16.0);

// If it's already past 4:00, wait until 4:00 tomorrow    
if (now > fourOClock)
{
    fourOClock = fourOClock.AddDays(1.0);
}

int msUntilFour = (int)((fourOClock - now).TotalMilliseconds);

// Set the timer to elapse only once, at 4:00.
t.Change(msUntilFour, Timeout.Infinite);

请注意,如果使用System.Threading.Timer,则TimerCallback指定的回调将在线程池(非UI)线程上执行 - 因此,如果您计划对UI执行某些操作在4:00,你必须适当地编组代码(例如,在Windows窗体应用程序中使用Control.Invoke,或在WPF应用程序中使用Dispatcher.Invoke

答案 1 :(得分:27)

从.NET 4.5开始,有一个非常干净的解决方案:

public async void ScheduleAction(Action action, DateTime ExecutionTime)
{
    await Task.Delay((int)ExecutionTime.Subtract(DateTime.Now).TotalMilliseconds);
    action();
}

这是一个没有async / await的解决方案:

public void Execute(Action action, DateTime ExecutionTime)
{
    Task WaitTask = Task.Delay((int)ExecutionTime.Subtract(DateTime.Now).TotalMilliseconds);
    WaitTask.ContinueWith(_ => action);
    WaitTask.Start();
}

应该注意的是,由于int32最大值,这仅适用于大约24天,这对于您的情况很有用,但值得注意。

答案 2 :(得分:6)

您可以使用Task Sceduler on windows有关详细信息,请参阅daily trigger example

如果您想自己编写,请使用以下代码:

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
}

答案 3 :(得分:4)

以VoteCoffees为首,这是一个基于紧凑事件的解决方案:

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;
}

消费者:`

void Main()
{
    var trigger = new DailyTrigger(16); // every day at 4:00pm

    trigger.OnTimeTriggered += () => 
    {
        // Whatever
    };  

    Console.ReadKey();
}

答案 4 :(得分:0)

任务计划程序是一个更好的选择,它可以很容易地在C#中使用,http://taskscheduler.codeplex.com/

答案 5 :(得分:0)

我这样做是每天早上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;
        }

    }

答案 6 :(得分:0)

回应Dan的解决方案,使用Timercallback是一个快速而简洁的解决方案。 在要安排要运行的任务或子例程的方法内,请使用以下命令:

    t = New Timer(Sub()
                        'method call or code here'
                  End Sub, Nothing, 400, Timeout.Infinite)

使用&#39; Timeout.Infinite&#39;将确保回调仅在400ms后执行一次。我正在使用VB.Net。

答案 7 :(得分:0)

这个解决方案怎么样?

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

答案 8 :(得分:0)

.NET有很多计时器类,但是它们都占用相对于当前时间的时间跨度。在相对时间里​​,在启动计时器之前以及在计时器运行期间进行监视时,需要考虑很多事情。

  • 如果计算机进入待机状态怎么办?
  • 如果计算机时间改变了怎么办?
  • 如果到期时间是在夏令时过渡之后怎么办?
  • 如果用户在启动计时器后更改了计算机的时区,以至于到期前有一个新的时区过渡,而以前没有,那么该怎么办?

操作系统非常适合处理这种复杂性。在.NET上运行的应用程序代码不是。

对于Windows,NuGet软件包AbsoluteTimer包装了一个在绝对时间到期的操作系统计时器。