调用Dispatcher.Invoke()两次时出现死锁问题

3

我有两个调度程序:

dispatcher1
dispatcher2

现在,当我调用以下代码(这是我复杂代码的简化示例)时:
            var ret = dispatcher1.Invoke(() => {
                return dispatcher2.Invoke(() => {
                    return new object();
                });
            });

我会在死锁中运行。
调用
dispatcher1.Invoke()

目前正在等待中

DispatcherSynchronizationContext.Wait()

在调用dispatcher2之后

dispatcher2.Invoke()

正在等待中

DispatcherSynchronizationContext.Wait

我无法将Invoke-Calls更改为异步调用(BeginInvoke),因为我需要结果。

在.NET 4.0中并非如此-只有自从我更改为.NET 4.5以来才出现了这种情况。

有没有办法解决这个问题?


尝试使用DispatcherPriority.Send调用Invoke()。这将完全跳过调度程序队列。此外,如果dispatcher1dispatcher2可以是相同的,请使用CheckAccess()来确定是否可以直接调用委托。 - Mike Strobel
这并没有解决我的问题。当我使用 DispatcherPriority.Send 时,它仍然是一样的。 - BennoDual
1
最明显的罪魁祸首可能是由于在 dispatcher2 上运行时对 dispatcher1.Invoke 的调用导致死锁:dispatcher1.Invoke 阻塞等待 dispatcher2.Invoke,后者执行尝试回调到 dispatcher1.Invoke 的代码。基本上,从一个分派程序向另一个分派程序发出阻塞呼叫几乎等同于在请求死锁。 - Mike Strobel
为什么你有多个调度程序?应该只有一个UI线程。多余的线程几乎总是不必要的,会引起麻烦。 - usr
1个回答

4

这是一个非常糟糕的解决方案,你不应该使用它。

但如果你想冒险尝试,请尝试使用下面的InvokeAndPump扩展方法替换你的Invoke调用。(不要替换项目中的每一个调用Invoke,只替换问题中有问题的调用。)

public static class DispatcherExtensions
{
    public static T InvokeAndPump<T>(
        this Dispatcher dispatcher,
        Func<T> function,
        DispatcherPriority priority = DispatcherPriority.Normal)
    {
        if (dispatcher == null)
            throw new ArgumentNullException("dispatcher");
        if (function == null)
            throw new ArgumentNullException("function");

        // If you've found this code in your project, you are doomed.  <3

        Action wait, notify;

        var currentDispatcher = Dispatcher.FromThread(Thread.CurrentThread);
        if (currentDispatcher != null)
        {
            var frame = new DispatcherFrame();

            wait = () => Dispatcher.PushFrame(frame);
            notify = () => frame.Continue = false;
        }
        else
        {
            var waitEvent = new ManualResetEventSlim(false);

            wait = waitEvent.Wait;
            notify = waitEvent.Set;
        }

        var error = default(Exception);
        var result = default(T);

        dispatcher.BeginInvoke(
            priority,
            new Action(
                () =>
                {
                    try { result = function(); }
                    catch (Exception e) { error = e; }
                    finally { notify(); }
                }));

        // Hold on to your butts...

        wait();

        if (error != null)
            throw new TargetInvocationException(error);

        return result;
    }
}

严肃地说:天哪,如果有人使用这个并期望它能可靠地工作,那么他们会很痛苦。我只是出于好奇和邪恶的原因才发布它。主要是邪恶。

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