【问题标题】:Code for executing method every day at specific time C# (Windows Service) failed每天在特定时间执行方法的代码C#(Windows服务)失败
【发布时间】:2013-11-29 09:20:51
【问题描述】:

我每天早上 5 点都有这段代码来执行 windows 服务的方法:

编辑:

MyService ws = new MyService ();

protected override void OnStart(string[] args)
{
    if (serviceHost != null)
    {
        serviceHost.Close();
    }

    serviceHost = new ServiceHost(typeof(MyService));

    serviceHost.Open();
    double TimeOfExecution = 5;

    DateTime now = DateTime.Now;
    DateTime today5am = now.Date.AddHours(TimeOfExecution);
    DateTime next5am = now <= today5am ? today5am : today5am.AddDays(1);

    System.Threading.TimerCallback callback = new System.Threading.TimerCallback(ws.MethodToExecute());

    var timer1 = new System.Threading.Timer(callback, null, next5am - DateTime.Now, TimeSpan.FromHours(24));

 }

我预计服务会在下一个 5 点执行,之后每 24 小时执行一次。

方法MethodToExecute() 确实在当天 5 点(或其他指定时间)执行,但第二天它未能执行。此外,似乎我是否第一次执行它并不重要,但似乎服务在一段时间后进入睡眠状态并且不执行,所以如果 5 点到达不是当天,而是第二天,它会执行失败。

有谁知道可能出了什么问题?

【问题讨论】:

  • 你能发布更多代码吗?具体来说,您是否确保您的计时器可以持续到第二天?使用var表示局部变量...
  • 请注意,这不会处理时间更改。如果您有 DST,它可能会更改为早上 6 点或凌晨 4 点。
  • 如果您希望它在特定时间点运行,而不是将其作为服务,您可以考虑将其作为普通控制台应用程序,并使用 Windows 任务计划程序运行它
  • @MarceloBarbosa 我在服务中有几种方法,它们在不同的时间运行并做不同的事情(一种方法在早上 8 点运行,第二种在下午 3 点运行,第三种在晚上 9 点运行),所以我认为是这样设置比让三个控制台应用程序在不同的时间运行并做不同的事情更容易,特别是如果你有很多方法,我不知道,也许这只是个人喜好......

标签: c# timer windows-services


【解决方案1】:

GC 将收集您的 timer,因为在 OnStart 方法之后您没有任何对它的引用。

你只是把它当作一个局部变量。我希望您知道,一旦 JIT 表明它们不再在代码中使用,局部变量就有资格进行垃圾回收。

修复:只需将 timer 存储在实例变量中,就完成了。

private System.Threading.Timer my5AmTimer = null;

protected override void OnStart(string[] args)
{
    //All other code..

   this.my5AmTimer = new System.Threading.Timer(callback, null, next5am - DateTime.Now, TimeSpan.FromHours(24));
}

【讨论】:

  • 谢谢!但是,如果一旦离开 OnStart 方法就对计时器进行垃圾收集,那么该方法怎么可能第一次执行呢?
  • @user3048706 我希望您知道,一旦 JIT 表明它们不再在代码中使用,局部变量就有资格进行垃圾回收。符合 GC。 GC 在需要时运行。它不会一直运行。考虑阅读手册以获取更多信息。
  • @ta.speot.is 你应该再读一遍我的声明。我从未说过 GC 一直在运行。我说它是正确的垃圾收集合格。有没有cmets?
  • @ta.speot.is 你应该再读一遍我的声明。我从未说过 GC 一直在运行。我说它有资格进行垃圾收集,这是正确的。有cmets吗? – Sriram Sakthivel 1 分钟前 @SriramSakthivel 阅读我的评论。特别是我说GC在需要时运行它不会一直运行的部分。请特别注意我引用您的评论的部分,并且把重点放在合格这个词上。
  • @ta.speot.is 对不起,我想我误会了,所以你正在向 OP 提供更多信息,对吗?
【解决方案2】:
var timer1 = new System.Threading.Timer(...);

计时器是棘手的对象。当它们不再被引用时,它们就像任何 .NET 对象一样受到垃圾回收的影响。该语句的语法足以知道何时发生这种情况,您对var 的使用表明“timer1”是方法的局部变量。换句话说,在您的方法返回后,您创建的 Timer 对象没有任何引用。

那么,当没有 引用时,究竟是什么让 Timer 保持活跃和滴答作响?你发现:什么都没有。您的计时器是否会真正滴答作响是一个废话。如果您的程序继续运行做其他事情,那么它会触发 gen#0 集合,并且计时器将消失。它永远不会滴答作响。如果你的程序没有,那么它可以存活到凌晨 5 点。

必须将对象引用存储在一个变量中,以确保它存在足够长的时间。那应该是一个静态变量。

或者使用 System.Timers.Timer。它的作用很像 System.Threading.Timer,但具有更好的生存技能。只要它具有 Elapsed 事件处理程序并且已启用,它就可以保证保持活动状态。

【讨论】:

  • @Hans Passant 感谢您的帮助!并将我的 ws 变量从 MyService ws = new MyService();活得够久。我也应该把它设为静态吗?
  • 不可能猜到。不要随意将变量设为静态。听起来该计时器应该是 MyService 中的一个字段,因此只要服务处于活动状态,它就会保持活动状态。
【解决方案3】:

read the fine manual:

注意

只要您使用 Timer,就必须保留对它的引用。与任何托管对象一样,当没有对 Timer 的引用时,它会受到垃圾回收的影响。 Timer 仍然处于活动状态这一事实并不能阻止它被收集。

保留对它的引用:

MyService ws = new MyService ();

System.Threading.Timer timer;

protected override void OnStart(string[] args)
{
    ...

    if (timer1 != null)
        timer1.Dispose();

    timer1 = new System.Threading.Timer(callback, null,
        next5am - DateTime.Now, TimeSpan.FromHours(24));
 }

【讨论】:

    【解决方案4】:

    另一个选项可能是告诉 GarbageCollector 保持局部变量处于活动状态:

    GC.KeepAlive(timer1);
    

    【讨论】:

    • 这是不正确的。 KeepAlive 不会使局部变量超出其声明的范围,它只会确保对象在调用 KeepAlive 之后才符合垃圾回收条件。
    猜你喜欢
    • 1970-01-01
    • 2011-01-23
    • 2014-03-28
    • 2013-04-18
    • 2013-11-25
    • 2010-11-25
    • 1970-01-01
    • 2013-11-09
    • 1970-01-01
    相关资源
    最近更新 更多