WPF后台线程调用

WPF后台线程调用,wpf,multithreading,dispatcher,Wpf,Multithreading,Dispatcher,也许我记错了Winforms是如何工作的,或者我把它搞得太复杂了,但这是我的问题 我有一个WPF客户端应用程序,可以通过WCF与服务器对话。当前用户可以“注销”WPF客户端,这将关闭所有打开的屏幕,只留下导航窗格,并最小化程序窗口。当用户重新最大化程序窗口时,系统会提示他们登录。简单 但有时情况会发生在后台线程上,比如客户端每5分钟尝试进行一次WCF调用,刷新一些缓存数据。当这个5分钟计时器触发时,如果用户注销怎么办?那么,应该提示用户重新登录…这当然必须在UI线程上发生 private

也许我记错了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();
       }
    }
到目前为止还不错,对吧?但是当多个线程碰到这段代码时会发生什么呢

嗯,我的第一个直觉是,由于我正在应用程序.Current.Dispatcher中进行同步,所以我应该很好,无论哪个线程首先命中,都将负责显示登录表单并让用户登录

情况并非如此

  • 线程1将点击登录表单上的代码和调用ShowDialog

  • 线程2也会命中代码,并在线程1调用ShowDialog后立即调用Login,因为调用ShowDialog解除了线程1的阻塞(我相信是因为WPF消息泵的工作方式)

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

    我想要的只是一种让用户重新登录到应用程序的同步方式……我在这里遗漏了什么


    提前谢谢。

    也许有点锁

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

    还考虑缓存用户凭据而不是重新提示它们,例如SeCuScLe:


    PK:-)

    很抱歉延迟跟进

    几天前,我通过基本上为WPF实现DoEvents修复了UI线程上的阻塞问题:

    所以现在,许多线程,包括后台线程和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);
            }
    

    为了给Paul的回答增加一点细节,我相信他是说你应该锁定securityContext。这样,一旦一个线程访问并修改了securityContext(可能是登录的),那么下面的其他线程将不会弹出登录对话框,因为在访问securityContext时,securityContext已经设置好了。这一点很好。我应该说我试过…所以请考虑以下内容:1。后台线程进入并调用应用程序调度程序线程,同时锁定securityContextLock对象。2.用户单击以最大化应用程序,真正的UI线程进入并尝试访问securityContext…但它被锁定…因此整个UI线程冻结…甚至认为guy在步骤1中设法显示了登录表单,UI线程被冻结,用户无法与登录表单交互。所以我们有一个死锁。我还应该提到,忽略而不是阻止不是一个选项,因为任何请求securityContext的线程都会立即需要它来创建WCF通道凭据。如果应用程序处于“活动”状态,可能只显示后台进程的登录?但这并没有达到提示用户重新登录的目的……还是我误解了?