代码之家  ›  专栏  ›  技术社区  ›  Thomas Levesque

永远调用块

  •  3
  • Thomas Levesque  · 技术社区  · 14 年前

    我正在尝试调用UI调度程序上的对话:

    class DialogService : IDialogService
    {
        private readonly Dispatcher _dispatcher = Application.Current.Dispatcher;
    
        public bool? Show(IDialogViewModel viewModel)
        {
            if (_dispatcher.CheckAccess())
            {
                var dialogWindow = new DialogWindow();
                return dialogWindow.Show(viewModel);
            }
            else
            {
                Func<IDialogViewModel, bool?> func = Show;
                return (bool?)_dispatcher.Invoke(func, viewModel);
            }
        }
    }
    

    但是,电话 Invoke 永远阻塞,以及 Show 从未在UI线程上调用…

    使用 BeginInvoke 不是选项:我需要立即得到结果,因为我正在处理来自远程对象的事件(使用.NET远程处理)

    有什么想法吗?


    更新

    下面是对问题的更完整描述:

    我有一个客户端应用程序,它使用.NET远程处理与Windows服务通信。在某个时刻,客户机调用服务来执行操作(此调用由用户操作触发,在这种情况下单击按钮)。服务可能需要凭据来执行该操作:在这种情况下,它会引发 CredentialsNeeded 事件,由客户端处理。然后,客户机将显示一个对话框,提示用户提供凭据,并在事件参数中设置适当的属性。当事件处理程序返回时,服务使用凭据完成操作,并将控制权返回给客户端。

    所以,当我收到事件时,UI线程正在等待在服务端完成一个操作…我想这就是为什么 援引 呼叫未处理,但我如何处理它?我能创造 另一个 显示对话框的用户界面线程?在WinForms中,我知道我可以用 Application.Run 但我不知道如何在WPF中做到这一点…

    3 回复  |  直到 14 年前
        1
  •  4
  •   Jon Skeet    14 年前

    在这个方法调用期间,您拥有一个锁吗?UI线程上的另一个方法正在尝试获取?这当然可以解释。

    会发生这种事吗 每一个 时间?这显然会使诊断更容易。

    对于我来说,这是不寻常的,我建议去调试器:只需点击break,看看线程在做什么。

    最后,我知道你需要结果…但是如果你 呼叫 BeginInvoke 相反(并返回一个虚拟值)?是否调用调度程序中的方法?显然,这不是一个长期的修复,但它会提供更多的诊断信息。

        2
  •  1
  •   Josh    14 年前

    在您试图使用invoke时,UI线程是否正在对其他对象(可能是您的后台线程)进行阻塞调用?如果是这样,那么你的手上就有一个典型的死锁。两个线程,每个线程等待另一个线程返回。

    在Windows窗体中,它们经常在幕后进行“消息泵送”,这通常是在您最不希望出现死锁的情况下进行的,但很多时候它会造成更多的问题,并且由于意外的可重入性,很难找到错误。

    如果您不认为您的UI线程正在进行阻塞调用,那么应该在调试器中运行应用程序,并在死锁发生时闯入调试器。然后在线程窗口中查找主线程。双击主线程,然后查看调用堆栈窗口,查看主线程的位置。

    您也可以尝试显式地指定发送程序的优先级,尽管我认为如果存在真正的死锁,这并不重要。

        3
  •  1
  •   Thomas Levesque    14 年前

    我最终找到了解决我问题的方法:我只需要在一个新线程上显示对话框,并使用它自己的调度程序。以下是修改后的代码:

    class DialogService : IDialogService
    {
        private readonly Dispatcher _dispatcher = Application.Current.Dispatcher;
    
        public bool? Show(IDialogViewModel viewModel)
        {
            if (_dispatcher.CheckAccess())
            {
                DoShow(viewModel);
            }
            else
            {
                bool? r = null;
                Thread thread = new Thread(() => r = DoShow(viewModel));
                thread.SetApartmentState(ApartmentState.STA);
                thread.Start();
                thread.Join();
                return r;
            }
        }
    
        private static bool? DoShow(IDialogViewModel viewModel)
        {
            var dialogWindow = new DialogWindow();
            return dialogWindow.Show(viewModel);
        }
    }