线程内的计时器

本文关键字:计时器 线程 | 更新日期: 2023-09-27 18:01:36

我的目标:我有一个信用卡等待窗口。我将从客户端调用一个函数来等待信用卡刷卡。为了避免程序在等待信用卡时卡住。我使用委托来运行计时器。这个委托会调用一个计时器。计时器定期检查卡是否存在。如果它找到一张卡片,它将调用客户端分配的回调/委托。

的代码如下,我的问题是1)将_timer_Elapsed将在线程内调用,以便它将添加最小的开销到ui窗口?

2)我如何从定时器函数调用基类的回调/事件。我写了一个受保护的方法,它将在基类中调用事件/委托。我需要从定时器函数调用受保护的方法(这是在派生类的委托内部)?

Wait wait = delegate()
{
    _timer = new Timer(3000); // Set up the timer for 3 seconds
    _timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
    _timer.Enabled = true; // Enable it
    static void _timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        // if(CheckCardsPresence())
        {
            //RaiseEvent()
            //KillTimer()
        }
        //else
        {
            // do nothing. wait more
        }
    }
};
wait.Invoke();

线程内的计时器

不,计时器回调将在委托线程上执行。

  1. 怎么可能?计时器不能在线程上"闯入",该线程必须轮询。
  2. 这个委托线程将在启动计时器后立即终止。这意味着你根本不需要这个线程。除非没有显示代码。

当你使用System.Threading.Timer时,回调将被推入线程池。

第二个问题(尽量一次只问一个问题)

  • 受保护的成员应该可以通过匿名(嵌入式)方法访问。你有具体的问题吗?

从MSDN文档(对不起,我第一次得到错误的类)

这个Windows定时器是为单线程环境设计的,其中UI线程用于执行处理。它要求用户代码有一个可用的UI消息泵。

这是一种迂回的方式来说明事件将在UI线程/消息泵上引发,也就是说,你的第一个问题的答案是肯定的,只要你的"线程"指的是"UI线程"。

我不太明白你的第二个问题——你在说什么基类?

首先,该代码将无法编译。不能在另一个方法中声明命名方法。但是,您可以声明一个匿名方法或lambda表达式,然后将其分配给委托引用。

可能不需要对信用卡设备进行异步轮询。您可能能够使用System.Windows.Forms.Timer并从运行在UI线程上的Tick事件执行轮询。如果CheckCardsPresence是一个快速操作,这是可以接受的。

public class CreditCardWaitWindow : Form
{
  private System.Windows.Timer timer = new System.Windows.Timer();
  private void Form_Load(object sender, EventArgs args)
  {
    timer.Tick += OnTick;
    timer.Interval = 3000;
    timer.Start();
  }
  private void OnTick(object sender, EventArgs args)
  {
    if (CheckCardsPresence())
    {
      RaiseEvent();
      timer.Stop();
    }
  }
}

如果轮询信用卡设备是一个耗时的操作,那么你将希望在另一个线程上执行此操作,以避免阻塞UI。

public class CreditCardWaitWindow : Form
{
  private System.Timers.Timer timer = new System.Timers.Timer();
  private void Form_Load(object sender, EventArgs args)
  {
    timer.Elapsed += OnElapsed;
    timer.Interval = 3000;
    timer.AutoReset = false;
    timer.Start();
  }
  private void OnElapsed(object sender, ElapsedEventArgs args)
  {
    if (CheckCardsPresence())
    {
      Invoke(
        (MethodInvoker)(() =>
        {
          RaiseEvent();
        }), null);
    }
    else
    {
      timer.Start();
    }
  }
}

下面是一个使用Task的更简洁的实现。

public class CreditCardWaitWindow : Form
{
  private void Form_Load(object sender, EventArgs args)
  {
    Task.Factory.StartNew(
      () =>
      {
        while (true)
        {
          Thread.Sleep(3000);
          if (CheckCardsPresence()) break;
        }
      }, TaskCreationOptions.LongRunning).ContinueWith(
      () =>
      {
        RaiseEvent();
      }, TaskScheduler.FromCurrentSychronizationContext());
  }    
}
更重要的是,你可以在c# 5.01中使用新的await关键字。我不确定它能比这更简洁!
public class CreditCardWaitWindow : Form
{
  private async void Form_Load(object sender, EventArgs args)
  {
    while (!CheckCardsPresence()) await Task.Delay(3000);
    RaiseEvent();
  }
}

1 c# 5.0还没有发布