C# - System.Timers.Timer 的替代方案,在特定时间调用函数

发布于 2024-08-29 15:42:42 字数 1526 浏览 8 评论 0原文

我想在特定时间调用 C# 应用程序上的特定函数。起初我考虑使用 Timer (System.Time.Timer),但很快就变得无法使用。为什么?

简单的。 Timer 类需要一个以毫秒为单位的 Interval,但考虑到我可能希望执行该函数,假设在一周内,这意味着:

  • 7 天 = 168 小时;
  • 168 小时 = 10,080 分钟;
  • 10,080 分钟 = 604,800 秒;
  • 604,800 秒 = 604,800,000 毫秒;
  • 所以间隔是 604,800,000;

现在让我们记住 Interval 接受的数据类型是 int,并且我们知道 int 范围从 -2,147,483,648 到 2,147,483,647。

这使得Timer毫无用处,不是在这种情况下,而是在超过大约25天的情况下,一旦我们无法将Interval设置为大于2,147,483,647毫秒。< /strong>


所以我需要一个可以指定何时调用该函数的解决方案。 类似这样:

solution.ExecuteAt = "30-04-2010 15:10:00";
solution.Function = "functionName";
solution.Start();

因此,当系统时间达到“30-04-2010 15:10:00”时,该函数将在应用程序中执行。

如何解决这个问题?


其他信息:这些功能将做什么?

  • 获取气候信息并基于该信息:
  • 启动/关闭其他应用程序(大多数是控制台)基于);
  • 向这些控制台应用程序发送自定义命令;
  • 电脑断电、重启、睡眠、休眠;
  • 如果可能的话,安排 BIOS 启动计算机;

编辑:

Interval 接受的数据类型似乎是 double< /code>,但是,如果您将 Interval 设置为大于 int 的值,并调用 Start(),则会引发异常[0,Int32.MaxValue]

编辑 2:

Jørn Schou-Rode 建议使用 Ncron 来处理安排任务,乍一看这似乎是一个很好的解决方案,但我想听听一些使用过它的人的情况。

I want to call a specific function on my C# application at a specific time. At first I thought about using a Timer (System.Time.Timer), but that soon became impossible to use. Why?

Simple. The Timer class requires a Interval in milliseconds, but considering that I might want the function to be executed, let's says in a week that would mean:

  • 7 days = 168 hours;
  • 168 hours = 10,080 minutes;
  • 10,080 minutes = 604,800 seconds;
  • 604,800 seconds = 604,800,000 milliseconds;
  • So the interval would be 604,800,000;

Now let's remember that the Interval accepted data type is int, and as we know int range goes from -2,147,483,648 to 2,147,483,647.

That makes Timer useless, not in this case, but in the case of more than about 25 days, once we cannot set a Interval bigger that 2,147,483,647 milliseconds.


So I need a solution where I could specify when the function should be called. Something like this:

solution.ExecuteAt = "30-04-2010 15:10:00";
solution.Function = "functionName";
solution.Start();

So when the System Time would reach "30-04-2010 15:10:00" the function would be executed in the application.

How can this problem be solved?


Additional information: What will these functions do?

  • Getting climate information and based on that information:
  • Starting / Shutting down other applications (most of them console based);
  • Sending custom commands to those console applications;
  • Power down, rebooting, sleep, hibernate the computer;
  • And if possible schedule the BIOS to power up the computer;

EDIT:

It would seem that the Interval accepted data type is double, however if you set a value bigger that an int to the Interval, and call Start() it throws a exception [0, Int32.MaxValue].

EDIT 2:

Jørn Schou-Rode suggested using Ncron to handle the scheduling tasks, and at first look this seems a good solution, but I would like to hear about some who has worked with it.

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

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

发布评论

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

评论(8

挖鼻大婶 2024-09-05 15:42:42

您的“Start()”方法应该生成一个线程,该线程以定义的时间间隔唤醒,检查时间,如果没有达到所需的时间,则返回睡眠状态。

Your "Start()" method should spawn a thread that wakes up at a defined interval, checks the time, and if you haven't reached the desired time, goes back to sleep.

篱下浅笙歌 2024-09-05 15:42:42

任务调度的一种方法与 klausbyskov 提出的方法类似,是在现有的 .NET 调度框架/库之上构建调度服务。与使用 Windows 任务计划程序相比,这具有以下优点:(a) 允许在同一项目中定义多个作业,以及 (b) 将作业和调度逻辑“在一起”,即不依赖于容易迷失的服务器设置。系统升级/更换。

我知道有两个开源项目提供这种功能:

  • "Quartz.NET 是一个功能齐全的开源作业调度系统,可用于从最小的应用程序到大型企业系统。”我自己从未真正使用过这个框架,但通过研究该网站,我的印象是一个非常可靠的工具,提供了许多很酷的功能。 Stackoverflow 上有 [quartz-net] 标签这一事实也可能表明它

  • NCron 是一个轻量级库,用于在.NET 服务器平台。”它的功能数量没有 Quartz.NET 的一半,而且在 Stackoverflow 上也没有任何标签,但作者(真正的作者)相信,它的低摩擦 API 使其更容易上手。

在 NCron 之上构建调度服务,您可以使用一行代码安排每周执行的 CleanupJob

service.Weekly().Run<CleanupJob>();

好的,您将需要在此之上大约三行样板代码实际上将您的项目变成 Windows 服务,但当我声称可以用一行代码完成它时,听起来更令人印象深刻;)

One approach to task scheduling, simliar to that proposed by klausbyskov, is to built your scheduling service on top of an existing .NET scheduling framework/library. Compared to using the Windows Task Scheduler, this has the advantages of (a) allowing several jobs to be defined in the same project and (b) keeping jobs and scheduling logic "together" - i.e. not relying on server settings prone to get lost in system upgrades/replacements.

I know of two open-source projects that offer this kind of functionality:

  • "Quartz.NET is a full-featured, open source job scheduling system that can be used from smallest apps to large scale enterprise systems." I have never actually used this framework myself, but from studying the website, I have the impression of a very solid tool, providing many cool features. The fact that there [quartz-net] tag on Stackoverflow might also indicate that it is actually used in the wild.

  • "NCron is a light-weight library for building and deploying scheduled background jobs on the .NET server platform." It does not have half as many features as Quartz.NET, and it does not have any tag on Stackoverflow, but the author (yours truly) believes that its low-friction API makes it somewhat easier to get started with.

Building your scheduling service on top of NCron, you can schedule a CleanupJob for weekly execution using a single line of code:

service.Weekly().Run<CleanupJob>();

Ok, you will need around three lines of boiler plate code on top of that to actually turn your project into a Windows service, but it sounds more impressive when I claim that it can be done with one line of code ;)

生活了然无味 2024-09-05 15:42:42

我建议您只编写一个处理其业务部分的程序,然后在必要时使用Windows 任务计划程序执行该程序。

I would recommend that you just write a program that deals with the business part of it and then execute that program when necessary by using Windows Task Scheduler.

同尘 2024-09-05 15:42:42

您可以为带有 DateTime 实例的 Timer 编写某种包装类。然后执行以下步骤:

  1. 确定 DateTime.Now 与所需时间之间的差异。
  2. 如果差值(以毫秒为单位)大于 Timer.Interval 属性的最大允许值,请将 Interval 设置为最大允许值(即 double.MaxValue 或其他值)并启动它。
  3. 现在,当计时器第一次经过时,您只需返回到步骤 1。

有时,差异将小于 Interval 属性的最大允许值,然后您可以在包装器中触发一个事件,该事件最终调用所需的方法。

You could write some sort of wrapper class for a Timer which takes a DateTime instance. Then you perform the following steps:

  1. Determine the difference between DateTime.Now and the desired time.
  2. If the difference (in milliseconds) is larger than the maximum allowed value for the Timer.Interval property, set the Interval to the maximum allowed value (i.e. double.MaxValue or whatever) and start it.
  3. Now, when the timer elapses the first time, you simply go back to step 1.

At some time, the difference will be smaller than the maximum allowed value for the Interval property, and then you could fire an event in your wrapper which ultimately calls the desired method.

属性 2024-09-05 15:42:42

使用 System.Threading.Timer:

    var timer = new System.Threading.Timer(delegate { }, // Pass here a delegate to the method
        null,
        TimeSpan.FromDays(7), // Execute Method after 7 days.
        TimeSpan.Zero);

Use the System.Threading.Timer:

    var timer = new System.Threading.Timer(delegate { }, // Pass here a delegate to the method
        null,
        TimeSpan.FromDays(7), // Execute Method after 7 days.
        TimeSpan.Zero);
烟雨扶苏 2024-09-05 15:42:42

您可以使用 System.Threading.Timer 类,它提供了一个接受以 Int64 表示的间隔的构造函数,这应该足以满足您的需求。

现在来说其他的事情:

  • 您可以使用 Process 类启动/停止/配置程序(我真的不明白您所说的“自定义命令”)
  • 您无法重新启动或关闭或控制本地使用本机 .NET 类的 BIOS。通过 Interop(从 .NET 调用本机 Windows API)可以重新启动/重新启动,并且调度 BIOS 是不可能的。或者也许使用特殊的服务器主板?我不知道..

You can use the System.Threading.Timer class, which provides a constructor accepting an interval expressed as an Int64, which should be enough for your needs.

Now for the other stuff :

  • You can start/stop/configure program using the Process class (I don't really get what you call "custom commands")
  • You cannot restart or shut down or control the local BIOS using native .NET classes. Rebooting / restarting is possible through Interop (calling native Windows API from .NET), and scheduling the BIOS is just impossible. Or maybe with a special server motherboard ? I don't know..
怎会甘心 2024-09-05 15:42:42

System.Threading.Timer 类也有相同的限制(根据 MSDN)。

似乎没有 .Net Framework 类本身就擅长规避 Int32.MaxValue 毫秒上限。

public static class Scheduler
{
    private const long TimerGranularity = 100;

    static Scheduler()
     {
         ScheduleTimer = new Timer(Callback, null, Timeout.Infinite, Timeout.Infinite);
        Tasks = new SortedQueue<Task>();
     }

    private static void Callback(object state)
    {
        var first = Tasks.Peek();
        if(first.ExecuteAt<DateTime.Now)
        {
            Tasks.Dequeue();
            var executionThread = new Thread(() => first.Function());
            executionThread.Start();                
        }
    }

    private static Timer ScheduleTimer { get; set; }

    public static void Start()
    {
        ScheduleTimer.Change(0, TimerGranularity);
    }
    public static void Add(Task task)
    {
        Tasks.Enqueue(task);
    }

    public static SortedQueue<Task> Tasks { get; set; }
}

public class Task : IComparable<Task>
{
    public Func<Boolean> Function { get; set; }

    public DateTime ExecuteAt { get; set; }

    public int CompareTo(Task other)
    {
        return ExecuteAt.CompareTo(other.ExecuteAt);
    }
}

我使用的解决方案与上面的示例类似:一个类 Scheduler 管理所有 Task(为了避免我们为每个任务都有一个计时器)正在安排)。

任务被添加到能够执行排序插入的队列中。请注意,SortedQueue不是 .Net Framework 的一种类型,而是一个假设的、易于编码的集合,能够在类似类型 T 上进行排序插入。

调度程序会唤醒每个 TimerGranularity 毫秒并检查第一个任务的“ExecuteAt”时间已被超过;然后在单独的线程上执行它。

可以通过创建所有超越任务的列表(而不仅仅是第一个任务)来完成额外的工作;但为了清楚起见,我省略了它。

The class System.Threading.Timer has the same limitation too (it would throw an ArgumentOutOfRangeException according to MSDN).

There seems to be no .Net Framework class natively adept to circumvent the Int32.MaxValue milliseconds upper bound.

public static class Scheduler
{
    private const long TimerGranularity = 100;

    static Scheduler()
     {
         ScheduleTimer = new Timer(Callback, null, Timeout.Infinite, Timeout.Infinite);
        Tasks = new SortedQueue<Task>();
     }

    private static void Callback(object state)
    {
        var first = Tasks.Peek();
        if(first.ExecuteAt<DateTime.Now)
        {
            Tasks.Dequeue();
            var executionThread = new Thread(() => first.Function());
            executionThread.Start();                
        }
    }

    private static Timer ScheduleTimer { get; set; }

    public static void Start()
    {
        ScheduleTimer.Change(0, TimerGranularity);
    }
    public static void Add(Task task)
    {
        Tasks.Enqueue(task);
    }

    public static SortedQueue<Task> Tasks { get; set; }
}

public class Task : IComparable<Task>
{
    public Func<Boolean> Function { get; set; }

    public DateTime ExecuteAt { get; set; }

    public int CompareTo(Task other)
    {
        return ExecuteAt.CompareTo(other.ExecuteAt);
    }
}

The solution I'd use is something similar to the above example: a class Scheduler that manages all the Tasks (in order to avoid having a timer for each task we are going to schedule).

Tasks are added to the a queue able to perform sorted insertion. Note that SortedQueue<T> is not a type of the .Net Framework but an hypothetical, easy-to-code collection capable of sorted insertion on a comparable type T.

The scheduler awakes every TimerGranularity milliseconds and checks for the first task whose `ExecuteAt' time has been surpassed; then executes it on a separate thread.

Additional effort could be done by creating a list of all surpassed tasks (instead of the first one only); but I left it out for the sake of clarity.

人事已非 2024-09-05 15:42:42

有一个名为 Quartz.NET 的 nu-get。

您完全可以将其用于此目的。

There is exist exist nu-get called Quartz.NET.

You can use it exactly for this.

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