2010-04-27 61 views
3

也許我錯誤地記住了Winforms的工作方式,或者我正在過度複雜化,但這是我的問題。WPF背景線程調用

我有一個WPF客戶端應用程序通過WCF與服務器通話。當前用戶可以「註銷」WPF客戶端,該客戶端關閉所有打開的屏幕,僅保留導航窗格並最小化程序窗口。當用戶重新最大化程序窗口時,系統會提示他們登錄。簡單。

但是有時候事情發生在後臺線程上 - 就像客戶端每5分鐘嘗試一次WCF調用刷新一些緩存數據一樣。如果用戶在5分鐘計時器觸發時退出,該怎麼辦?那麼,應該提示用戶重新登錄...並且這當然必須在UI線程上發生。

private static ISecurityContext securityContext; 
    public static ISecurityContext SecurityContext 
    { 
     get 
     { 
      if (securityContext == null) 
      { 
       // Login method shows a window and prompts the user to log in 
       Application.Current.Dispatcher.Invoke((Action)Login); 
      } 
      return securityContext; 
     } 
    } 

    private static void Login() 
    { 
     if (securityContext == null) { \ 
     /* show login window and set securityContext */ 
     var w = new LoginWindow(); 
     w.ShowDialog(); 
     securityContext = w.GetSecurityContext(); 
     } 
    } 

到目前爲止很好,對不對?但是當多個線程碰到這段代碼時會發生什麼?

嗯,我的第一個直覺是,因爲我在Application.Current.Dispatcher中同步,我應該沒問題,並且任何線程先打的負責顯示登錄窗體並讓用戶登錄。 。

不是這樣的......

  1. 線程1會打碼,並調用ShowDialog的登錄表單上

  2. 線程2也將打碼,並會盡快致電登錄線程1已經調用ShowDia日誌,因爲調用ShowDialog的暢通線程1(我相信的,因爲方式的WPF消息泵作品)

...最終效果是,我有彈出用戶同時多個登錄表單。

我想要的是讓用戶重新登錄到應用程序的同步方式......我在這裏錯過了什麼?

在此先感謝。

回答

1

很抱歉的延遲跟進。

我基本上通過實施的DoEvents爲WPF幾天前固定的阻塞問題在UI線程: http://khason.net/blog/how-to-doevents-in-wpf/

所以,現在,許多線程,背景和UI可以調用到UI線程,如果窗口已經顯示,將「模擬」ShowDialog的行爲,但不阻止並且不顯示第二個登錄窗口......希望對任何人閱讀都有意義。

void ShowLoginWindow(Window window) 
      { 
       if (window != null) 
       { 
        if (window.Visibility != Visibility.Visible) 
        { 
         try 
         { 
          result = window.ShowDialog(); 
         } 
         catch (Exception ex) 
         { 
         } 
        } 
        else 
        { 
         // don't block the UI thread, but wait till the dialog window returns 
         while(window.Visibilit y== Visibility.Visible) 
         { 
          DoEvents(); 
         } 
         return window.DialogResult; 
        } 
       } 
       return result; 
     } 

     void DoEvents() 
     { 
      DispatcherFrame f = new DispatcherFrame(); 
      Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, 
      (SendOrPostCallback)delegate(object arg) 
      { 
       DispatcherFrame fr = arg as DispatcherFrame; 
       fr.Continue = false; 
      }, f); 
      Dispatcher.PushFrame(f); 
     } 
1

也許有點鎖定?

您可以監視條目,並可能忽略(不阻止)其他輪詢操作。使用單一接入點只顯示一次登錄表單,並等待...

http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim.aspx

另外,考慮緩存用戶憑據,而不是再次提示他們,例如SecureString的:

http://msdn.microsoft.com/en-us/library/system.security.securestring.aspx

PK :-)

+0

給Paul的答案增加一點細節,我相信他說你應該鎖定securityContext。這樣,一旦一個線程訪問並修改了securityContext(大概是登錄),那麼其他跟隨的線程將不會彈出登錄對話框,因爲securityContext在訪問時已經被設置。 – Dave 2010-04-27 04:06:38

+0

好點。我應該提到我嘗試過....請考慮以下事項:1.後臺線程進入並調用Application Dispatcher線程,同時鎖定securityContextLock對象。 2.用戶單擊以最大化應用程序,並且真正的UI線程進入並嘗試訪問securityContext ...但它被鎖定....所以整個UI線程凍結...甚至認爲步驟1中的人管理爲了顯示登錄表單,UI線程被凍結,並且用戶不能與登錄表單交互。所以我們陷入僵局。 – Jeff 2010-04-27 04:17:57

+0

我還應該提到忽略而不是阻塞不是一個選項,因爲任何要求securityContext的線程都會立即需要它來創建WCF通道憑證。 – Jeff 2010-04-27 04:32:42