Улучшение кода: лучшие альтернативы этому шаблону? - PullRequest
3 голосов
/ 31 августа 2011

В похожем вопросе:
Как называется эта модель?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 ]

3 голосов
/ 31 августа 2011

Есть ряд проблем с этим шаблоном. Если вы хотите вызвать обработчик только один раз, вы должны сделать что-то вроде этого:

 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 голос
/ 31 августа 2011

Исходный код является достаточным (и очень легким) способом предотвращения рекурсии в однопоточном приложении. Так что, если во время вашей функции обработки событий вы можете войти в код, который может запускать событие снова, вы не будете входить в бесконечную рекурсию.

Но кода недостаточно для предотвращения доступа из нескольких потоков из-за вероятности возникновения гонки. Если вам нужно убедиться, что только один поток может запустить это событие, вам следует использовать более сильный механизм блокировки, такой как Mutex или Semaphore.

1 голос
/ 31 августа 2011

Я буду использовать что-то вроде:

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

Выполнение вызовет внутреннее логическое значение для предотвращения повторного входа.В распоряжении этот флаг будет сброшен.Execute вызовет лямбду, только если флаг равен false.Это делается для того, чтобы флаг становился ложным, даже если происходит исключение (приводящее к тому, что обработчик не выполняется), и, возможно, его немного лучше увидеть.Конечно, это не потокобезопасно, как исходный код, но это потому, что мы говорим о предотвращении двойного выполнения из потока того же .

0 голосов
/ 31 августа 2011

Следующее работает в однопоточных и многопоточных сценариях и является безопасным для исключений ... также, если необходимо, его можно изменить, чтобы обеспечить определенный уровень повторного входа (например, 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);
}
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...