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

发布于 2024-10-08 23:16:13 字数 124 浏览 0 评论 0原文

我希望在我的应用程序中触发一个事件,该事件在一天中的某个时间(例如下午 4:00)连续运行。我考虑每秒运行一次计时器,当时间等于下午 4:00 时运行该事件。那行得通。但我想知道是否有办法只在下午 4:00 回调一次,而不必继续检查。

I would like to have an event triggered in my app which runs continuously during the day at a certain time, say at 4:00pm. I thought about running the timer every second and when the time is equal to 4:00pm run the event. That works. But I'm wondering if there's a way to just get the callback once at 4:00pm and not having to keep checking.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

天煞孤星 2024-10-15 23:16:13

怎么样,使用 System.Threading。定时器类?

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)线程上执行,因此如果您如果计划在 4:00 对 UI 执行某些操作,则必须适当地编组代码(例如,在 Windows 窗体应用程序中使用 Control.InvokeDispatcher.Invoke代码 > 在 WPF 应用程序中)。

How about something like this, using the System.Threading.Timer class?

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

Note that if you use a System.Threading.Timer, the callback specified by TimerCallback will be executed on a thread pool (non-UI) thread—so if you're planning on doing something with your UI at 4:00, you'll have to marshal the code appropriately (e.g., using Control.Invoke in a Windows Forms app, or Dispatcher.Invoke in a WPF app).

爺獨霸怡葒院 2024-10-15 23:16:13

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

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

这是一个没有异步/等待的解决方案:

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

应该注意的是,由于 int32 最大值,这仅适用于大约 24 天,这对于您的情况来说已经足够了,但值得注意。

Starting with .NET 4.5 there's a really clean solution:

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

Here's a solution without async/await:

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

It should be noted that this only works for about 24 days out because of int32 max value, which is plenty for your case, but worth noting.

缺⑴份安定 2024-10-15 23:16:13

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

/// <summary>
/// Utility class for triggering an event every 24 hours at a specified time of day
/// </summary>
public class DailyTrigger : IDisposable
{
    /// <summary>
    /// Time of day (from 00:00:00) to trigger
    /// </summary>
    TimeSpan TriggerHour { get; }

    /// <summary>
    /// Task cancellation token source to cancel delayed task on disposal
    /// </summary>
    CancellationTokenSource CancellationToken { get; set; }

    /// <summary>
    /// Reference to the running task
    /// </summary>
    Task RunningTask { get; set; }

    /// <summary>
    /// Initiator
    /// </summary>
    /// <param name="hour">The hour of the day to trigger</param>
    /// <param name="minute">The minute to trigger</param>
    /// <param name="second">The second to trigger</param>
    public DailyTrigger(int hour, int minute = 0, int second = 0)
    {
        TriggerHour = new TimeSpan(hour, minute, second);
        CancellationToken = new CancellationTokenSource();
        RunningTask = Task.Run(async () => 
        {
            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, CancellationToken.Token);
                OnTimeTriggered?.Invoke();
            }
        }, CancellationToken.Token);
    }

    /// <inheritdoc/>
    public void Dispose()
    {
        CancellationToken?.Cancel();
        CancellationToken?.Dispose();
        CancellationToken = null;
        RunningTask?.Dispose();
        RunningTask = null;
    }

    /// <summary>
    /// Triggers once every 24 hours on the specified time
    /// </summary>
    public event Action OnTimeTriggered;

    /// <summary>
    /// Finalized to ensure Dispose is called when out of scope
    /// </summary>
    ~DailyTrigger() => Dispose();
}

消费者:`

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

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

Taking VoteCoffees lead, here is a compact event based solution:

/// <summary>
/// Utility class for triggering an event every 24 hours at a specified time of day
/// </summary>
public class DailyTrigger : IDisposable
{
    /// <summary>
    /// Time of day (from 00:00:00) to trigger
    /// </summary>
    TimeSpan TriggerHour { get; }

    /// <summary>
    /// Task cancellation token source to cancel delayed task on disposal
    /// </summary>
    CancellationTokenSource CancellationToken { get; set; }

    /// <summary>
    /// Reference to the running task
    /// </summary>
    Task RunningTask { get; set; }

    /// <summary>
    /// Initiator
    /// </summary>
    /// <param name="hour">The hour of the day to trigger</param>
    /// <param name="minute">The minute to trigger</param>
    /// <param name="second">The second to trigger</param>
    public DailyTrigger(int hour, int minute = 0, int second = 0)
    {
        TriggerHour = new TimeSpan(hour, minute, second);
        CancellationToken = new CancellationTokenSource();
        RunningTask = Task.Run(async () => 
        {
            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, CancellationToken.Token);
                OnTimeTriggered?.Invoke();
            }
        }, CancellationToken.Token);
    }

    /// <inheritdoc/>
    public void Dispose()
    {
        CancellationToken?.Cancel();
        CancellationToken?.Dispose();
        CancellationToken = null;
        RunningTask?.Dispose();
        RunningTask = null;
    }

    /// <summary>
    /// Triggers once every 24 hours on the specified time
    /// </summary>
    public event Action OnTimeTriggered;

    /// <summary>
    /// Finalized to ensure Dispose is called when out of scope
    /// </summary>
    ~DailyTrigger() => Dispose();
}

Consumer:`

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

    trigger.OnTimeTriggered += () => 
    {
        // Whatever
    };  
    
    Console.ReadKey();
}
思慕 2024-10-15 23:16:13

您可以使用Windows 上的任务计划程序有关详细信息,请参阅每日触发示例

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

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
}

You can use Task Sceduler on windows See daily trigger example for detail.

or use bellow code if you want wrote it yourself:

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
}
骑趴 2024-10-15 23:16:13

.NET 有很多计时器类,但它们都需要相对于当前时间的时间跨度。对于相对时间,在启动计时器之前需要考虑很多事情并在计时器运行时进行监控。

  • 如果计算机进入待机状态怎么办?
  • 如果计算机的时间改变了怎么办?
  • 如果到期时间是在夏令时转换之后怎么办?
  • 如果用户在启动计时器后更改计算机的时区,以便在到期之前出现以前不存在的新时区转换,该怎么办?

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

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

.NET has lots of timer classes, but they all take time spans relative to the current time. With a relative time, there are lots of things to consider before you start your timer and to monitor for while your timer is running.

  • What if the computer goes into a standby state?
  • What if the computer's time changes?
  • What if the expiration time is after a daylight savings time transition?
  • What if the user changes the computer's time zone after you started your timer such that there is a new time zone transition before expiration that wasn't previously there?

The operating system is in a great place to handle this complexity. Application code running on .NET is not.

For Windows, the NuGet package AbsoluteTimer wraps an operating system timer that expires at an absolute time.

今天小雨转甜 2024-10-15 23:16:13

我每天早上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;
        }

    }

I did this way to fire 7am every morning

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

    }
绾颜 2024-10-15 23:16:13

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

Task scheduler is a better option, and it can be easily used in C#, http://taskscheduler.codeplex.com/

音盲 2024-10-15 23:16:13

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

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

使用“Timeout.Infinite”将确保回调仅在 400 毫秒后执行一次。我正在使用VB.Net。

Echo to Dan's solution, using Timercallback is a quick and neat solution.
Inside the method you want to schedule a task or subroutine to be run, use the following:

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

use of 'Timeout.Infinite' will ensure the callback will be executed only once after 400ms. I am using VB.Net.

情仇皆在手 2024-10-15 23:16:13

这个解决方案怎么样?

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

How about this solution?

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文