Dispatcher.Invoke永远阻止

时间:2010-06-22 16:49:08

标签: wpf invoke dispatcher

我正在尝试在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的调用将永久阻止,并且永远不会在UI线程上调用Show ...

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

有什么想法吗?


更新

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

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

因此,当我收到事件时,UI线程正在等待服务端的操作完成...我认为这就是为什么没有处理Invoke调用的原因,但是我怎么工作周围 ?我可以创建另一个 UI线程来显示对话框吗?在WinForms中,我知道我可以使用Application.Run启动另一个消息泵,但我不知道如何在WPF中执行相同操作...

3 个答案:

答案 0 :(得分:4)

你是否在这个方法调用期间拥有一个锁,这是UI线程上的另一个方法试图获取的?这当然可以解释它。

这是时间发生的吗?这显然会使诊断变得更容易。

对我来说不同寻常,我建议去调试器:只需点击中断,看看线程在做什么。

最后,我知道你需要结果...但是如果你而不是调用BeginInvoke并返回一个虚拟值会发生什么?这会调用调度程序中的方法吗?显然这不是一个长期修复,但它会提供更多的诊断信息。

答案 1 :(得分:1)

当您尝试使用Invoke时,UI线程是否阻止调用其他东西(可能是您的后台线程)?如果是这样,那么你手上就会出现经典的僵局。两个线程各等待另一个返回。

在Windows窗体中,他们经常在幕后“消息抽取”中做很多事情,当你最不希望它是为了避免死锁时,但很多时候它会产生更多的问题,并且由于意外的重入而很难找到错误。

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

您也可以尝试显式指定DispatcherPriority of Send虽然我认为如果存在真正的死锁则无关紧要。

答案 2 :(得分:1)

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

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);
    }
}