【问题标题】:WPF Background Thread InvocationWPF 后台线程调用
【发布时间】:2010-04-27 03:42:40
【问题描述】:

也许我记错了 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 消息泵的工作方式)

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

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

提前致谢。

【问题讨论】:

    标签: wpf multithreading dispatcher


    【解决方案1】:

    也许有点锁定?

    您可以监控条目,也可以忽略(而不是阻止)其他轮询操作。使用单一入口点只显示一次登录表单并等待...

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

    另外,考虑缓存用户凭据而不是重新提示它们,例如安全字符串:

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

    PK :-)

    【讨论】:

    • 为保罗的回答添加更多细节,我相信他是说你应该锁定securityContext。这样一来,一旦一个线程访问并修改了securityContext(推测为已登录),那么后面的其他线程将不会弹出登录对话框,因为在它们获得访问权时已经设置了securityContext。
    • 好点。我应该提到我尝试过......所以请考虑以下问题: 1. 一个后台线程进入并调用 Application Dispatcher 线程,同时锁定一个 securityContextLock 对象。 2. 用户点击最大化应用程序,真正的 UI 线程进来并试图获取 securityContext ......但它被锁定了......所以整个 UI 线程冻结......甚至认为第 1 步中的人管理为了显示登录表单,UI 线程被冻结,用户无法与登录表单交互。所以我们陷入了僵局。
    • 我还应该提到,忽略而不是阻塞不是一种选择,因为任何请求 securityContext 的线程都将立即需要它来创建 WCF 通道凭据。
    • 如果应用程序处于“活动状态”,也许只显示后台进程的登录?
    • 但这并没有达到提示用户重新登录的目的......还是我误解了?
    【解决方案2】:

    很抱歉耽搁了跟进。

    几天前,我通过基本实现 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);
            }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多