2012-02-22 3 views
0

다음 코드는 mdi 형식으로 창을 만드는 코드입니다. 아이디어는 존재하는 경우 특정 유형의 창을 작성하거나 이미 인스턴스가있는 경우 앞에 가져 오는 것입니다.Wpf 창 만들기 잠금

public static object CreateWindow(Type windowType, params object[] args) 
    { 
     try 
     { 
      lock (_definitionToWindow) 
      { 
       var def = new WindowDefinition {ControlType = windowType, Args = args}; 

       System.Windows.Forms.Form win = null; 
       if (_definitionToWindow.TryGetValue(def, out win)) 
       {      
        win.Activate(); 
        return win; 
       }  

       System.Windows.Controls.Control uiElement = 
        (System.Windows.Controls.Control) Activator.CreateInstance(windowType, args); 


       object result = null; 
       if (uiElement is Window) 
        result = WpfMdiHelper.ShowWpfWindowInMdi((Window) uiElement); 
       else 
        result = WpfMdiHelper.ShowWpfControlInMdi((System.Windows.Controls.Control) uiElement); 

       if (result is System.Windows.Forms.Form) 
       {      
        _definitionToWindow.Add(def, result as System.Windows.Forms.Form); 
        lock (_windowslock) 
        { 
         _windows.Add((System.Windows.Forms.Form) result, uiElement as IHasViewModel);       
        } 
        ((System.Windows.Forms.Form) result).Disposed += new EventHandler(WindowsFactory_Disposed); 
       }     
       return result; 
      } 
     } 
     catch (Exception ex) 
     { 
      Logger.WriteError("Window creation exception", ex.ToString(), LogEntryCodes.UIException); 
     } 
     return null; 
    } 

코드 다소 작동하지만 당신은 여러 창을 열어 빠르게 연속 창을 여러 종류를 여는 버튼을 클릭합니다.

디버그 추적을 실행 한 후에 lock (_definitionToWindow)이 모든 클릭 (동일한 스레드에서 모든 호출이 이루어지는 것처럼 보임)과 메서드 블록 Activator.CreateInstance에서 무시된다는 것을 발견했습니다. 따라서 두 번째 호출이 사전에 도착하면 이전 인스턴스를 찾지 않고 창을 다시 만듭니다.

왜 이런 일이 발생하는지 알고 계십니까? 그리고이 상황을 처리하는 올바른 방법은 무엇입니까?

+0

조금 더 많은 정보를 제공하려면 wtf가 동일한 코드에서 두 번 수행하는 스레드가 동일한 스레드 (잠금이 작동하지 않는 이유 설명)가 아닌 것 같습니다. –

+1

STA 스레드에서 * lock *을 사용하는 것은 불법입니다. CLR은 메시지 루프를 펌핑하여이를 보완합니다. 클릭과 같이 Windows 메시지에 의해 트리거 된 이벤트의 재진입 실행이 발생합니다. UI 스레드에서 UI 관련 코드 만 실행하십시오 (필요한 경우 Dispatcher.BeginInvoke()). 그러므로 * lock *을 사용할 필요가 없습니다. –

+0

그 정보를 주셔서 감사합니다, 내가 해왔 던 대부분의 윈도우 프로그래밍은 ASP.NET에 있었기 때문에 나는 그것을 몰랐습니다. 이 경우에는 실제로 스레드에서 실행되는 UI 관련 코드입니다. Form을 작성하고 리턴하지만 다른 방법으로는 그렇지 않습니다. –

답변

3

동일한 스레드에 있어도 하나의 호출자 만 CreateWindowImpl에 허용하는 스레드 안전 잠금을 제공해야합니다. lock()과 달리 스레드를 차단하지 않습니다.

static long Locked = 0; 

static void CreateWindow(...) 
{ 
    if(0 == Interlocked.Exchange(ref Locked, 1)) 
    { 
     try 
     { 
     CreateWindowImpl(...); 
     } 
     finally 
     { 
     Interlocked.Exchange(ref Locked, 0); 
     } 
    } 
} 
+0

확실하게 해결 하겠지만, 같은 스레드가 두 번있는 이유는 무엇입니까? –

+0

Activator.CreateInstance가 이벤트 전달을 허용하여 CreateWindow를 다시 입력 할 수 있다고 상상해보십시오. 아마도 필요한 것은 부울 플래그 (스레드 안전성이없는) 일 것입니다. – Phil

+2

이 MSDN 기사의 맨 아래에 http://msdn.microsoft.com/en-us/library/ms741870.aspx에서 "WPF의 작업은 메모리 누수를 다시 도입하지 않고 예기치 않은 재진입을 피하는 것입니다. 우리는 어디에서나 재진입을 막지 않습니다. " – Phil