【问题标题】:System.Timers.Timer delay hangs applicationSystem.Timers.Timer 延迟挂起应用程序
【发布时间】:2011-08-12 14:18:40
【问题描述】:

我对 System.Timers.Timer 对象有疑问。我使用计时器对象定期执行任务。在计时器构造函数中,我调用执行工作的方法( DoTimeCheck() ),以确保任务在启动时也运行一次。工作(定期)在 BackgroundWorker 中完成。

我用这个来调用计时器:

 UpdaterTimer ut = UpdaterTimer.UpdaterTimerInstance;

我的问题是我需要将任务的第一次运行延迟 3 分钟(在应用程序启动时运行的那个)。后续运行(Elapsed 事件)应立即运行。我想通过调用来做到这一点

System.Threading.Thread.Sleep(TimeToDelayFirstRunInMiliseconds);

但这失败了,因为它还会挂起应用程序的 UI(主线程),使其无法使用。如何在不挂起 UI 的情况下延迟 DoTimeCheck() 的第一次运行? 定时器的代码如下。如果问题没有以清晰的方式呈现,请告诉我,我会编辑。提前谢谢你。

  public sealed class UpdaterTimer : Timer
{
    private static readonly UpdaterTimer _timer = new UpdaterTimer();
    public static UpdaterTimer UpdaterTimerInstance
    {
        get { return _timer; }
    }

    static UpdaterTimer()
    {
        _timer.AutoReset = true;
        _timer.Interval = Utils.TimeBetweenChecksInMiliseconds;
        _timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
        _timer.Start();
        DoTimeCheck();

    }

    static void _timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        DoTimeCheck();
    }

    private static void DoTimeCheck()
    {
        //... work here 
    }
}

【问题讨论】:

  • 全班都这样吗? (只是对使用所有静态方法从 Timer 继承感到好奇)
  • 感谢您的评论 - 除了 DoTimeCheck() 中的代码之外,这是相关的整个类。所有的静态都应该确保只运行一个计时器——一个单例。当然,这可能不是从 System.Timers.Timer 继承的最佳方式(我是新手),但到目前为止它已经奏效了。

标签: c# timer


【解决方案1】:

这样做的一种方法是给 Timer Interval 一个初始值(例如 3 分钟)。然后,在您的 Elapsed 事件处理程序中,您可以将间隔更改为从那时起使用的常规值。

 _timer.Interval = Utils.InitialCheckInterval;

 static void _timer_Elapsed(object sender, ElapsedEventArgs e)
 {
     if (_timer.Interval == Utils.InitialCheckInterval)
     {
         _timer.Interval = Utils.RegularCheckInterval;
     }

     DoTimeCheck();
 }

【讨论】:

  • 您不想将 Utils.TimeBetween... 与 Utils.RegularInterval 交换吗?
  • 不要这么认为——计时器将被设置为初始值。然后第一次经过的时间间隔将更改为常规间隔。
  • 我认为你在我和你的评论之间编辑了它,我怀疑你只是在编辑后才看到我的评论。但是,是的,现在看起来不错。 :)
  • @Chris 我做了 - 但我没有交换它们,只是让名称更清晰。想想我们俩的想法都是一样的。
  • @Peter Kelly:是的,我们在同一个页面上,所以一切都很好。至少它表明使名称更清晰是值得的。 ;-)
【解决方案2】:

看来(尽管您没有显示该代码)您正在主/GUI 线程上调用Sleep(TimeToDelayFirstRunInMiliseconds);,所以这就是导致您的 UI 线程挂起的原因。相反,您应该将计时器设置为在第一次运行时延迟 3 分钟,然后在它运行后再次更改计时器以在所有后续运行中以您希望的频率运行。

【讨论】:

    【解决方案3】:

    您的 UI 驻留在同一个线程上,因此当您将线程置于睡眠状态时,它也会导致您的 UI 挂起。您需要在不同的线程上运行计时器。

    【讨论】:

    • 感谢您的评论 - 我只使用 BackgroundWorker 来完成长期工作。除此之外,我远离多线程,因为这对我来说是薄冰,我对多线程还没有很好的理解。
    • 您将在这里需要它。不过,在 .NET 中进行多线程处理有一些安全的方法,所以不要气馁。看看这个链接:msdn.microsoft.com/en-us/library/ms951089.aspx
    【解决方案4】:

    您似乎已经在使用计时器了。在启动另一个计时器之前,只需使用另一个计时器延迟三分钟即可。

    timer = new Timer();
    timer.AutoReset = false;
    timer.Interval = 3*60*1000;
    timer.Elapsed += startOtherTimerMethod;
    timer.Start();
    

    编辑:我应该注意到,这与彼得凯利的回答大致相同,只是他的解决方案更优雅,因为它只使用一个计时器,没有额外的方法,并利用了以下事实:计时器在运行之间是可变的。如果你喜欢这个答案,你会喜欢他的。 ;-)

    【讨论】:

      【解决方案5】:

      您的 UI 需要一个单独的线程,目前您还在休眠 UI。检查this post

      【讨论】:

        【解决方案6】:

        在这种情况下你不应该使用 thread.sleep 你应该使用 winforms 控件

        从不锁定主 UI 的BackgroundWorker。你可以在那里写你的逻辑。

        这里的例子: http://www.knowdotnet.com/articles/backgroundworker.html

        【讨论】:

        • 谢谢,但我已经使用 BackgroundWorker 来完成所有工作。我已经尝试在 BackgroundWorker 中添加 sleep 方法,但是:1 - UI 仍然挂起,2 - 这意味着每个 timer.Elapsed() 事件都会发生延迟,我只需要延迟第一次运行。
        【解决方案7】:

        使用System.Threading.Timer - 构造函数采用第一次运行的延迟参数和后续运行的间隔。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-03-29
          • 1970-01-01
          • 2021-10-16
          • 2015-01-23
          • 1970-01-01
          • 2015-05-29
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多