3

也许我记错了 Winforms 的工作原理,或者我把这件事搞得太复杂了,但这是我的问题。

我有一个 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 调用 ShowDialog 后立即调用 Login,因为调用 ShowDialog 未阻塞线程 1(我相信是因为 WPF 消息泵的工作方式)

...最终效果是我一次向用户弹出了多个登录表单。

我想要的只是让用户重新登录到应用程序的同步方式......我在这里错过了什么?

提前致谢。

4

2 回答 2

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 :-)

于 2010-04-27T03:56:12.267 回答
1

抱歉延迟跟进。

几天前,我通过基本上为 WPF 实现 DoEvents 在 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);
        }
于 2010-05-07T03:18:36.397 回答