Dispatcher.Invoke永远阻塞

4

我正在尝试在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 remoting)。有什么想法吗?
更新:我有一个客户端应用程序,使用.NET Remoting与Windows服务通信。在某个时刻,客户端调用服务执行操作(在这种情况下,由用户操作触发,即单击按钮)。服务可能需要凭据才能执行操作:在这种情况下,它会引发一个CredentialsNeeded事件,由客户端处理。然后,客户端显示一个对话框以提示用户输入凭据,并设置事件参数中的适当属性。当事件处理程序返回时,服务使用凭据完成操作,并将控件返回给客户端。
因此,当我接收到事件时,UI线程正在等待服务端完成操作...我认为这就是Invoke调用未被处理的原因,但是我该如何解决?我可以创建另一个UI线程来显示对话框吗?在WinForms中,我知道我可以使用Application.Run启动另一个消息泵,但我不知道如何在WPF中做同样的事情...
3个回答

4
您在此方法调用期间是否拥有一个锁,而另一个UI线程上的方法正在尝试获取该锁?这可能是原因。
这种情况是否每次都发生?这显然会更容易诊断。
与我平时不同,我建议使用调试器:只需打断点并查看线程正在做什么。
最后,我知道您需要结果...但如果您改为调用BeginInvoke(并返回虚拟值),会发生什么?这是否会在调度程序中调用该方法?显然,这不是长期解决方案,但它将提供更多诊断信息。

谢谢你的回答。在代码的那部分我没有进行任何显式锁定,所以我不认为问题出在那里。是的,它每次都会发生...实际上,我想我开始理解出了问题出在哪里(请参见我的更新问题),只是我不知道该如何修复它... - Thomas Levesque

2

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

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

BeginInvoke or InvokeAsync would have fixed your problem. Multiple STA threads can be smelly. By the way you have now possibly blocked your worker thread with thread.Join(); - user585968
@MickyD 正如我在问题中提到的那样,BeginInvoke 不是一个选项,因为我需要同步获取对话框结果。无论如何,这是很久以前的事情了,而且这个解决方案对我来说有效 ;) - Thomas Levesque

1

UI线程是否在调用其他东西(也许是你的后台线程)时阻塞了?如果是这样,那么你手头上就有一个经典的死锁问题。两个线程都在等待另一个返回。

在Windows Forms中,他们会在幕后进行大量的“消息泵”,通常在你最不希望它发生的时候,以避免死锁,但很多时候它会创建更多的问题和难以找到的错误,因为出现了意外的重入。

如果你认为你的UI线程没有在进行阻塞调用过程中,你应该在调试器中运行应用程序,并在死锁发生时打断点。然后在线程窗口中查找主线程。双击主线程,然后查看调用堆栈窗口,以查看主线程所在的位置。

你也可以尝试明确指定Send的DispatcherPriority,但我认为如果存在真正的死锁,这并不重要。


谢谢你的回答。事实上,我的主线程正在对一个远程对象进行阻塞调用,而该远程对象本身又会向客户端发出一个阻塞调用... 远程对象正在触发一个事件以提示用户输入登录凭证。这就是我试图显示一个对话框的地方。请参阅我更新的问题以获取详细信息。 - Thomas Levesque

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接