【问题标题】:How do you control event firing in C#?如何在 C# 中控制事件触发?
【发布时间】:2011-09-28 22:08:48
【问题描述】:

我编写了一个程序,对通过网络摄像头传送的图像进行一些处理。每当从相机接收到新帧时,都会触发一个事件。但是,这种情况发生的频率比我想的要频繁 - 如此频繁,以至于我的图像处理函数在新事件出现并再次调用相同的函数之前没有完成。

如何控制事件何时触发?我可以改为每 5 个事件执行一次图像处理吗?我相信我已经弄清楚了伪代码,但我更愿意在 C# 中看到一些示例。

【问题讨论】:

  • 该伪代码将有助于理解您的问题。
  • 回调函数是否需要线程安全?

标签: c# events


【解决方案1】:

将事件回调“保护”如下。这样它就不会同时进行多次处理。

private bool m_active;
void YourCallback(object sender, EventArgs args) 
{
  if(!m_active) 
  {
    try 
    { 
      m_active = true;
      // Do the work here... 
    }
    finally { m_active = false; }
  }
}

编辑:如果使用 f.i,线程安全。 Semaphore.

private System.Threading.Semaphore m_Semaphore = new System.Threading.Semaphore(0, 1);
void YourCallback(object sender, EventArgs args) 
{
  if(m_Semaphore.WaitOne(0)) 
  {
    try 
    { 
      // Do the work here... 
    }
    finally { m_Semaphore.Release(); }
  }
}

【讨论】:

  • @Sam 正确。它认为这不会是什么大问题。但你永远不知道。添加了线程安全版本。
  • 我可能会锁定而不是使用信号量...信号量不能保证线程会按照它们排队的顺序进入。
  • 原来的问题不想让线程排队,而是如果回调还在执行就立即返回。
【解决方案2】:

您可以使用IObservable.SkipWhile 在图像处理过程中跳过到达您的处理程序的事件。

这个问题有类似的问题:

Filtering a Touch.FrameReported IObservable using arbitrary boolean condition that changes over time

【讨论】:

    【解决方案3】:

    那么你得到的伪代码是什么?我很想把它翻译成 C#。 ;-)

    您可以使用 DateTime 变量来存储最后一次发生的事件,然后在少于所需时间时跳过该事件。

    所以,如果你希望它每秒工作一次,不管它被触发多少次,这样的事情:

    private DateTime _lastEvent = DateTime.Now;
    
    public void Event()
    {
        if (_lastEvent + new TimeSpan(0, 0, 1) > DateTime.Now)
            return;
    
        _lastEvent = DateTime.Now;
    
        // Now do your event
        Console.WriteLine("Tick! " + _lastEvent);
    }
    

    【讨论】:

      【解决方案4】:

      假设您使用类似于以下内容的方式附加到事件:

      public MyObject()
      {    
         MyImageObject.Update += new UpdateEventHandler(ImageDataUpdated); 
      }
      
      private void ImageDataUpdated(object sender, EventArgs e) 
      {
          // do stuff
      }
      

      您可以在事件处理程序的开头与事件分离,然后在一定时间间隔后使用timer 重新附加。这将使您对更新速率有一定程度的精确控制。比如:

      public MyObject()
      {    
         MyTimer = new System.Timers.Timer(100); // 10 Hz
         MyTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
         MyTimer.Enabled = true;
      }
      
      private void ImageDataUpdated(object sender, EventArgs e) 
      {
         // detach from the event to keep it from fireing until the timer event has fired.
         MyImageObject.Update -= new UpdateEventHandler(ImageDataUpdated);
      
          // do stuff
      }
      
      private static void OnTimedEvent(object source, ElapsedEventArgs e)
      {
          // (re-)attach to the event handler.
         MyImageObject.Update += new UpdateEventHandler(ImageDataUpdated); 
      }
      

      使用此策略有一个很好的变化,即您可以防止底层图像对象在事件处理程序分离时执行额外的工作(当然这取决于图像对象的实现)。您可能会为自己的图像处理节省 CPU 周期。

      【讨论】:

      • 2 票反对,没有 cmets?请告诉我我的回答有什么错误,以便我有机会从我的错误中学习..
      • 为了弄清楚上面的逻辑是否是不好的做法,我为此打开了一个特定的问题。它位于此处:stackoverflow.com/questions/7594948/…
      猜你喜欢
      • 2019-06-20
      • 1970-01-01
      • 1970-01-01
      • 2014-04-20
      • 1970-01-01
      • 2018-11-18
      • 2017-11-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多