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

时间:2010-04-23 14:57:14

标签: c# timer scheduled-tasks

我想在特定时间调用我的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毫秒。


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

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

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

如何解决这个问题?


其他信息:这些功能将起什么作用?

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

修改

似乎Interval接受的数据类型为double,但是如果您设置的值大于intInterval,并调用{{1}它抛出异常Start()

编辑2:

JørnSchou-Rode建议使用Ncron来处理调度任务,起初看起来这似乎是一个很好的解决方案,但我想听听一些使用它的人。

8 个答案:

答案 0 :(得分:12)

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

答案 1 :(得分:11)

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

答案 2 :(得分:10)

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

我知道有两个提供此类功能的开源项目:

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

  • NCron是一个轻量级的库,用于在.NET服务器平台上构建和部署预定的后台作业。”它没有Quartz.NET多一半的功能,并且它在Stackoverflow上没有任何标签,但是作者(你们真的)认为它的低摩擦API使它更容易上手。

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

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

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

答案 3 :(得分:3)

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

  1. 确定DateTime.Now与所需时间之间的差异。
  2. 如果差异(以毫秒为单位)大于Timer.Interval属性的最大允许值,请将Interval设置为允许的最大值(即double.MaxValue或其他值)并启动它。
  3. 现在,当计时器第一次过去时,您只需返回步骤1.
  4. 在某个时候,差异将小于Interval属性的最大允许值,然后您可以在包装器中触发一个最终调用所需方法的事件。

答案 4 :(得分:3)

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

答案 5 :(得分:0)

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

现在换其他东西:

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

答案 6 :(得分:0)

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

似乎没有.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<T>不是.Net Framework的一种类型,而是一种假设的,易于编码的集合,能够在类似的类型T上对插入进行排序。

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

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

答案 7 :(得分:0)

存在称为Quartz.NET的nu-get。

您可以完全使用它。