2011-08-31 76 views
3

在一個類似的問題:
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; 
    } 
    } 

看來,大部分談話是圍繞爲什麼要一個不應該做這樣的中心,所以我覺得這個問題提供了一個更好的論壇來解決這個問題,並解決所有的問題。處理這個問題的更好/正確的方法是什麼?

回答

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

如果re-rais發生在同一個線程上(這是A.R.所討論的場景),你確定這個鎖是否可用? –

+1

你可能也想讓'_isHandled'變成volatile。或者,更好的辦法是避免重複檢查鎖定,只檢查'lock'塊內的'_isHandled'。 – LukeH

+0

@LukeH如果你檢查'lock'塊中的_isHandled狀態,有時調用線程將被凍結,直到'MyOtherPossiblyRecursiveMethod()'完成並且塊被解鎖。在這裏檢查兩次會更好。 – Artemix

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); 
} 
} 
+0

再次,如果我們在同一個線程聯鎖沒有任何東西互鎖... –

+0

不知道你看到什麼問題...這個代碼只是爲了與單線程和多線程場景...如果OP通常使用單線程,它根本不會受到傷害,並且可以達到他想要的效果......作爲一個「獎勵」,當移動到多線程設計時,他可以保持代碼相同......另一個好處是是異常安全的... – Yahia

1

我會使用類似:

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

的執行將提高內部布爾防止重新進入。在處置該標誌將被重置。只要標誌爲false,Execute就會調用lambda。這是爲了確保即使發生異常(導致處理程序從未執行)標誌也會變爲false,並且可能稍微好看一點。當然這不是線程安全的,就像原來的代碼一樣,但是這是因爲我們正在談論防止線程從相同的重複執行。

1

原始代碼是防止單線程應用程序中的遞歸的一種充分(並且非常輕量級)的方式。因此,如果在您的事件處理函數中,您可能會遇到可能再次觸發事件的代碼,則不會進入無限遞歸。

但是由於潛在的競爭條件,代碼不足以阻止來自多個線程的訪問。如果你需要確保只有一個線程運行這個事件,那麼你應該使用更強大的鎖定機制,如互斥鎖或信號量。