代码之家  ›  专栏  ›  技术社区  ›  Fábio Antunes

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

  •  19
  • Fábio Antunes  · 技术社区  · 15 年前

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

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

    • 7天=168小时;
    • 168小时=10080分钟;
    • 10080分钟=604800秒;
    • 604800秒=604800毫秒;
    • 所以区间为604800万;

    现在让我们记住 间隔 接受的数据类型为 int 我们知道 int 范围从-2147483648到2147483647。

    这使得 计时器 没用,不是在这种情况下,而是在超过25天的情况下,一旦我们不能设置 间隔 大于2147483647毫秒。


    所以我需要一个解决方案,我可以在其中指定何时调用函数。 像这样:

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

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

    这个问题怎么解决?


    附加信息:这些功能将做什么?

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

    编辑:

    似乎 间隔 接受的数据类型为 double 但是,如果设置的值大于 int 间隔 和呼叫 Start() 它抛出了一个异常 [0, Int32.MaxValue] .

    编辑2:

    J_ RN Schou Rode建议使用 Ncron 要处理调度任务,乍一看,这似乎是一个很好的解决方案,但我想听听有谁与之合作过。

    8 回复  |  直到 8 年前
        1
  •  10
  •   Community CDub    8 年前

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

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

    • Quartz.NET 是一个功能齐全的开源作业调度系统,可以从最小的应用程序到大规模的企业系统。“我从来没有真正使用过这个框架,但从研究这个网站,我有一个非常坚实的工具的印象,提供了许多很酷的功能。事实上 [quartz-net] stackoverflow上的标记还可能指示它实际上是在野外使用的。

    • NCron 它是一个轻量级的库,用于在.NET服务器平台上构建和部署计划的后台作业。“它没有Quartz.net一半的功能,在StackOverflow上也没有任何标记,但作者(您的作者)相信,它的低摩擦API使它更容易入门。

    在NCron之上构建您的调度服务,您可以调度 CleanupJob 对于使用单行代码的每周执行:

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

    好吧,你需要三行以上的样板代码才能将你的项目真正变成一个Windows服务,但当我声称可以用一行代码来完成时,这听起来更令人印象深刻;)

        2
  •  12
  •   Adrian    15 年前

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

        3
  •  11
  •   Klaus Byskov Pedersen    15 年前

    我建议您编写一个处理IT业务部分的程序,然后在必要时使用 Windows任务计划程序 .

        4
  •  3
  •   gehho    15 年前

    您可以为采用日期时间实例的计时器编写某种包装类。然后执行以下步骤:

    1. 确定datetime.now和所需时间之间的差异。
    2. 如果差异(毫秒)大于Timer.Interval属性的最大允许值,请将间隔设置为最大允许值(即Double.MaxValue或其他值),然后启动它。
    3. 现在,当计时器第一次超时时,您只需返回到步骤1。

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

        5
  •  3
  •   Islam Yahiatene    15 年前

    使用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);
    
        6
  •  0
  •   Shtong    15 年前

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

    其他方面:

    • 您可以使用 Process 类(我不真正了解您所称的“自定义命令”)。
    • 不能使用本机.NET类重新启动、关闭或控制本地BIOS。通过interop(从.NET调用本机Windows API)重新启动/重新启动是可能的,而调度BIOS则是不可能的。或者用一个特殊的服务器主板?我不知道…
        7
  •  0
  •   Erik Burigo    15 年前

    班级 System.Threading.Timer 也有相同的限制(它会抛出 ArgumentOutOfRangeException 根据 MSDN )

    似乎没有一个.NET框架类本机擅长规避 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 S(为了避免为我们要安排的每项任务设置计时器)。

    任务被添加到能够执行排序插入的队列中。注意 SortedQueue<T> 不是.NET框架的类型,而是一个假设的、易于代码收集的类型,它能够在可比较的类型T上对插入进行排序。

    调度员唤醒 TimerGranularity 毫秒,并检查其“executeat”时间为 超越 ;然后在单独的线程上执行它。

    可以通过创建所有 超越 任务(而不仅仅是第一个任务);但为了清晰起见,我把它省略了。

        8
  •  0
  •   Andrew_STOP_RU_WAR_IN_UA    8 年前

    存在一个名为quartz.net的存在。

    你可以用它来做这个。