Wpf创建窗口锁

时间:2012-02-22 12:00:49

标签: c# wpf thread-safety

我以下代码以mdi形式创建窗口。这个想法是创建一个特定类型的窗口,如果它存在,或者如果已经存在实例则将它带到前面。

  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上阻塞。因此,当第二个调用到达字典时,检查它找不到任何先前的实例并继续重新创建窗口。

任何人都知道为什么会这样?以及处理这种情况的正确方法?

1 个答案:

答案 0 :(得分: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);
      }
   }
}