【问题标题】:How to block a timer while processing the elapsed event?如何在处理经过的事件时阻止计时器?
【发布时间】:2009-05-02 16:10:59
【问题描述】:

我有一个计时器,它不需要同时处理其经过的事件处理程序。但是处理一个 Elapsed 事件可能会干扰其他事件。我实施了以下解决方案,但感觉有些不对;似乎我应该以不同的方式使用计时器或在线程空间中使用另一个对象。计时器似乎最合适,因为我确实需要定期检查状态,但有时检查会比我的间隔时间长。这是解决这个问题的最佳方法吗?

// member variable
private static readonly object timerLock = new object();
private bool found = false;


// elsewhere
timer.Interval = TimeSpan.FromSeconds(5).TotalMilliseconds;
timer.Elapsed = Timer_OnElapsed;
timer.Start();


public void Timer_OnElapsed(object sender, ElapsedEventArgs e)
{
  lock(timerLock)
  {
    if (!found)
    {
      found = LookForItWhichMightTakeALongTime();
    }
  }
}

【问题讨论】:

  • 这不应该是一个社区维基。
  • @Samuel - 如果发布者愿意,任何问题都可以成为社区 wiki。 FAQ 仅指定某些问题从一开始就应该是社区 wiki,但并不限制将 CW 应用于任何问题。
  • @tvanfosson:我让他知道像这样的问题不应该成为社区维基。
  • @Samuel - 我的意思是,在提出问题时没有“不应该”,在某些情况下只有“应该”。如果他们想要一个不会“要求”它的问题,则由 OP 选择 CW。如果你说“不需要”,那我就不会狡辩了。

标签: c# .net multithreading timer


【解决方案1】:

您可以将 AutoReset 设置为 false,然后在完成处理后显式重置计时器。当然,您如何处理它实际上取决于您期望计时器如何操作。这样做会使您的计时器偏离实际指定的时间间隔(就像停止和重新启动一样)。您的机制将允许每个间隔触发和处理,但它可能会导致未处理事件的积压,这些事件现在在接近导致调用处理程序的计时器到期时处理。

timer.Interval = TimeSpan.FromSeconds(5).TotalMilliseconds;
timer.Elapsed += Timer_OnElapsed;
timer.AutoReset = false;
timer.Start();


public void Timer_OnElapsed(object sender, ElapsedEventArgs e)
{
    if (!found)
    {
      found = LookForItWhichMightTakeALongTime();
    }
    timer.Start();
}

【讨论】:

  • AutoReset 和 Elapsed 用于 System.Threading.Timer,这对 UI 工作不安全。
  • @Samuel -- Elapsed 是 OP 正在使用的事件。大概这不是 WinForms 应用程序。
【解决方案2】:

我通常在处理计时器时停止计时器,进入 try/finally 块,并在完成后恢复计时器。

【讨论】:

    【解决方案3】:

    如果LookForItWhichMightTakeALongTime() 需要很长时间,我建议不要使用System.Windows.Forms.Timer,因为这样做会锁定您的 UI 线程,并且用户可能会认为您的应用程序已冻结而将其杀死。

    您可以使用BackgroundWorker(如果需要,还可以使用Timer)。

    public class MyForm : Form
    {
      private BackgroundWorker backgroundWorker = new BackgroundWorker();
    
      public MyForm()
      {
        InitializeComponents();
        backgroundWorker.DoWork += backgroundWorker_DoWork;
        backgroundWorker.RunWorkerCompleted +=
                                    backgroundWorker_RunWorkerCompleted;
        backgroundWorker.RunWorkerAsync();
      }
    
      private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
      {
        e.Result = LookForItWhichMightTakeALongTime();
      }
    
      private void backgroundWorker_RunWorkerCompleted(object sender,
                                                 RunWorkerCompletedEventArgs e)
      {
        found = e.Result as MyClass;
      }
    }
    

    您可以从任何您想要的地方拨打RunWorkerAsync(),如果您愿意,甚至可以拨打Timer。只需确保检查 BackgroundWorker 是否已经在运行,因为在运行时调用 RunWorkerAsync() 会引发异常。

    private void timer_Tick(object sender, EventArgs e)
    {
      if (!backgroundWorker.IsBusy)
        backgroundWorker.RunWorkerAsync();
    }
    

    【讨论】:

      【解决方案4】:
      timer.enabled = false
      

      timer.stop();
      

      timer.enabled = true
      

      timer.start();
      

      【讨论】:

        【解决方案5】:

        我像这样使用 System.Threading.Timer

         class Class1
            {
                static Timer timer = new Timer(DoSomething,null,TimeSpan.FromMinutes(1),TimeSpan.FromMinutes(1));
        
                private static void DoSomething(object state)
                {
                    timer = null; // stop timer
        
                    // do some long stuff here
        
                    timer = new Timer(DoSomething, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
                }
        
        
        
            }
        

        【讨论】:

        • 当你用完它们后,你应该总是释放 Timer 实例
        • 只要垃圾收集器没有清理它,定时器就会一直触发回调方法。这可能会导致意外的、不确定的影响。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-09-23
        • 1970-01-01
        • 2023-04-08
        • 1970-01-01
        • 2015-06-23
        • 2011-07-30
        • 1970-01-01
        相关资源
        最近更新 更多