代码改进:更好地替代这种模式?

时间:2011-08-31 16:04:45

标签: c# design-patterns

在类似的问题中:
What is this pattern called? Soft Lock?

我在询问下面代码清单的模式名称。

  public class MyClass
  {
    public event EventHandler MyEvent;
    private bool IsHandlingEvent = false;

    public MyClass()
    {
      MyEvent += new EventHandler(MyClass_MyEvent);
    }

    void MyClass_MyEvent(object sender, EventArgs e)
    {
      if (IsHandlingEvent) { return; }

      IsHandlingEvent = true;
      {
        // Code goes here that handles the event, possibly invoking 'MyEvent' again.
        // IsHandlingEvent flag is used to avoid redundant processing.  What is this
        // technique, or pattern called.
        // ...
      }
      IsHandlingEvent = false;
    }
  }

似乎大多数谈话都集中在为什么我们不应该这样做,所以我认为这个问题提供了一个更好的论坛来解决问题并解决所有问题。处理这个问题的更好/正确方法是什么?

4 个答案:

答案 0 :(得分:3)

该模式存在一系列问题。如果你只想调用一次处理程序,你可以这样做:

 protected static object _lockObj = new object();
 protected static bool _isHandled = false;    

 void MyClass_MyEvent(object sender, EventArgs e)
 {
     if(_isHandled)
       return;

     lock(_lockObj)
     {
         if(_isHandled)
            return;

         _isHandled = true;

         MyOtherPossiblyRecursiveMethod(); // Actually does all your work

         _isHandled = false;
     }
 }

 void MyOtherPossiblyRecursiveMethod()
 {
 }

这样,只有一个线程应该能够访问实际的工作方法。

答案 1 :(得分:1)

我将使用类似的东西:

using( var sl = new SoftLock() )
{
   sl.Execute(()=>{....});
}

执行将引发内部布尔值以防止重新进入。在处置中,该标志将被重置。如果标志为false,则Execute将调用lambda。这是为了确保即使发生异常(即导致处理程序从未执行),标志也会变为false,并且可能会更好看。当然这不像原始代码那样是线程安全的,但这是因为我们讨论的是阻止从相同的线程执行双重执行。

答案 2 :(得分:1)

原始代码是一种足够(并且非常轻量级)的方法来防止单线程应用程序中的递归。因此,如果在事件处理函数期间您可能会遇到可能再次触发事件的代码,则不会进入无限递归。

但由于竞争条件的可能性,代码不足以阻止来自多个线程的访问。如果您需要确保只有一个线程可以运行此事件,那么您应该使用更强大的锁定机制,如互斥锁或信号量。

答案 3 :(得分:0)

以下适用于单线程和多线程场景,并且是异常安全的......如果需要,也可以修改它以允许一定程度的重入(例如3个级别)......

public class MyClass
{
public event EventHandler MyEvent;
private int IsHandlingEvent = 0;

public MyClass()
{
  MyEvent += new EventHandler(MyClass_MyEvent);
}

void MyClass_MyEvent(object sender, EventArgs e)
{
 // this allows for nesting if needed by comparing for example < 3 or similar
 if (Interlocked.Increment (ref IsHandlingEvent) == 1 )
 {
  try {
      }
  finally {};  
 } 

 Interlocked.Decrement (ref IsHandlingEvent);
}
}