C# 如何在处理经过的事件时阻止计时器?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/815104/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 02:16:03  来源:igfitidea点击:

How to block a timer while processing the elapsed event?

c#.netmultithreadingtimer

提问by Michael Hedgpeth

I have a timer that needs to not process its elapsed event handler at the same time. But processing one Elapsed event mayinterfere with others. I implemented the below solution, but something feels wrong; it seems like either I should be using the timer differently or using another object within the threading space. The timer seemed to fit best because I do need to periodically check for a status, but sometimes checking will take longer than my interval. Is this the best way to approach this?

我有一个不需要同时处理其经过的事件处理程序的计时器。但是处理一个 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();
    }
  }
}

采纳答案by tvanfosson

You could set AutoReset to false, then explicitly reset the timer after you are done handling it. Of course, how you handle it really depends on how you expect the timer to operate. Doing it this way would allow your timer to drift away from the actual specified interval (as would stopping and restarting). Your mechanism would allow each interval to fire and be handled but it may result in a backlog of unhandled events that are handled now where near the expiration of the timer that cause the handler to be invoked.

您可以将 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();
}

回答by GregC

I usually stop the timer while processing it, enter a try/finally block, and resume the timer when done.

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

回答by Brad

timer.enabled = false

or

或者

timer.stop();

and

timer.enabled = true

or

或者

timer.start();

回答by Gary

I use the System.Threading.Timer like so

我像这样使用 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));
        }



    }

回答by Samuel

If LookForItWhichMightTakeALongTime()is going to take a long time, I would suggest not using a System.Windows.Forms.Timerbecause doing so will lock up your UI thread and the user may kill your application thinking that it has frozen.

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

What you could use is a BackgroundWorker(along with a Timerif so desired).

您可以使用的是一个BackgroundWorkerTimer如果需要,还可以使用)。

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;
  }
}

And you can call RunWorkerAsync()from anywhere you want to, even from a Timerif you want. And just make sure to check if the BackgroundWorkeris running already since calling RunWorkerAsync()when it's running will throw an exception.

您可以RunWorkerAsync()从任何您想要的地方拨打电话,即使您愿意也可以拨打电话Timer。并且确保检查它是否BackgroundWorker已经在运行,因为RunWorkerAsync()在它运行时调用会引发异常。

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