【问题标题】:Is there a way to prevent an event handler from firing over itself?有没有办法防止事件处理程序自行触发?
【发布时间】:2014-02-21 22:16:11
【问题描述】:

我有一个可以快速连续发生的事件的事件处理程序。在某些情况下,当事件再次触发时(毫秒内),处理程序仍将执行。

有没有办法让这些处理程序...串行?那么,在事件调用 #1 完成之前,事件调用 #2 无法启动?

如果这是一个糟糕的想法,为什么?这是一个在后台无人值守运行的批处理过程,因此我不太担心一个调用阻塞另一个调用可能会出现性能问题。

【问题讨论】:

  • 请注意,您需要有喘息的空间,否则如果要堆叠它们,最终可能会耗尽资源。
  • 事件处理程序是否在多个线程上运行?如果不是,事件处理程序是否在执行某种创建重入的 DoEvents 调用?如果不是,那么这怎么可能?事件处理程序是通过抽取消息队列来触发的;如果线程忙于运行事件处理程序,那么根据定义它不忙于抽队列!

标签: c# events


【解决方案1】:

使用多线程控件lock。这可以防止您的代码在同一时刻多次运行。

已编辑
示例:

public class Foo
{
    private static object _lockKey = new object();

    private void YourEventHandlerMethod(object sender, EventArgs e)
    {   
        lock (_lockKey)
        {
            // the code you put inside this block will be execute only
            // once at time.

            // If a second call has the intention to use this block before
            // the conclusion of the first call, the second call (the thread)
            // will be put in hold.
        }
    }
}

【讨论】:

  • 但是我要锁定什么?整个对象?我可以“锁定”一个方法(事件处理程序本身)吗?
  • 哦。所以,你可以把任何对象放在那里,因为对象实际上只是代码块正在使用的标志,对吧?
  • 如果处理程序在线程池上运行,当你锁定池中的所有线程时要小心死锁
  • 是的。但是锁只能对真正相同的对象起作用,即我将 _lockKey 写为静态的。
  • 请注意,您的事件处理程序仍然可以递归多次输入!如果您的事件代码本身再次触发同一线程上的事件,则会发生这种情况,因为它已经获得锁定并且可以重新进入它而无需重新锁定。这听起来有些牵强,但在 UI 事件中很常见,例如在 TextBox.TextChanged 事件中更改 Text 时。
【解决方案2】:

有几个选项可以序列化处理可能从多个线程并发触发的事件,或者通过重新注册/递归在同一线程上触发。

其中一个选项是使用任务调度程序。在您的场景中,任务调度程序的目的是将回调排队以供以后串行执行。类似的任务调度程序的一个众所周知的概念示例是 WinForms 消息循环(以Application.Run 开始,以Control.BeginInvoke 排队)和 WPF 调度程序循环(以Dispatcher.Run 开始,以Dispatcher.BeginInvoke 或Dispatcher.InvokeAsync 排队)。

TPL 任务调度程序的一个鲜为人知但非常有用的实现是 Stephen Toub 的StaTaskScheduler。我基于 here 和 here 发布了一些代码(后者来自一个密切的问题,Queuing Actions/Delegates for Asyncronous Execution)。

此处描述了另一个选项:Task sequencing and re-entracy。

在这种情况下,处理程序被组成为任务,每个新的处理程序都会在前一个任务完成时启动。

【讨论】:

    猜你喜欢
    • 2014-07-22
    • 2021-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-21
    • 2021-08-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多