C# WPF中的死锁:如何不阻止HwndHost.BuildWindowCore中的GUI线程? 我们有一个大的代码库,用于处理和可视化C++中的图像。现在,用户希望在此基础上进行构建,但他们的代码基础是.NETCore3.1和WPF。我们使用PInvoke来连接本机代码,并且可以非常成功地加入项目。少数本机小部件嵌入了包装器。一切都很好,我们很高兴我们的里程数

C# WPF中的死锁:如何不阻止HwndHost.BuildWindowCore中的GUI线程? 我们有一个大的代码库,用于处理和可视化C++中的图像。现在,用户希望在此基础上进行构建,但他们的代码基础是.NETCore3.1和WPF。我们使用PInvoke来连接本机代码,并且可以非常成功地加入项目。少数本机小部件嵌入了包装器。一切都很好,我们很高兴我们的里程数,c#,c++,wpf,multithreading,async-await,C#,C++,Wpf,Multithreading,Async Await,只有一个拦截器问题:HwndHost方法BuildWindowCore有时会在死锁中挂起。我们可以确定死锁的根本原因: 当BuildWindowCore调用Qt将本机小部件重新设置为托管小部件句柄的父对象时,我们使用阻塞调用来确保重新设置父对象的操作完成。但是,在重新设置小部件的父级期间,Qt有时调用DefWindowProc,将未处理的窗口消息传递回父WPF小部件。由于WPF线程被调用Qt阻塞,这是一个循环阻塞等待,以死锁结束 虽然我能理解这个问题,但我们对WPF GUI线程了解不够,无法解决

只有一个拦截器问题:
HwndHost
方法
BuildWindowCore
有时会在死锁中挂起。我们可以确定死锁的根本原因:

BuildWindowCore
调用Qt将本机小部件重新设置为托管小部件句柄的父对象时,我们使用阻塞调用来确保重新设置父对象的操作完成。但是,在重新设置小部件的父级期间,Qt有时调用
DefWindowProc
,将未处理的窗口消息传递回父WPF小部件。由于WPF线程被调用Qt阻塞,这是一个循环阻塞等待,以死锁结束

虽然我能理解这个问题,但我们对WPF GUI线程了解不够,无法解决这个问题

我们迄今为止所做的尝试:

  • 在后台调用Qt(使用
    wait
    ),但
    BuildWindowCore
    不能是异步方法
  • 将重新建立父子关系移出
    BuildWindowCore
    ,稍后将其称为
    async
    ,但是
    HwndHost
    小部件要求在
    BuildWindowCore
    中进行重新建立父子关系,否则我们会收到一个WPF错误,即本机小部件句柄不是(尚未)WPF小部件的子小部件
我有点不知所措了。我们可以在C++端对本机代码进行非阻塞调用,并在C循环中完成对其的轮询。但是,当我们轮询Qt以重新设置小部件的父级时,如何将控制权交还给wpfgui线程呢

最密切相关的建议是使用
wait Dispatcher.Yield(DispatcherPriority.ApplicationIdle)
,但这是一种
async
方法

在伪代码中,我考虑的是:

protected override HandleRef BuildWindowCore(HandleRef HWNDParent)
{
    NativeCode.beginReParenting(HWNDParent.Handle);

    while (!NativeCode.reParentingCompleted()) {
        // This method is async, is it clean to call it like this?
        Dispatcher.Yield(DispatcherPriority.ApplicationIdle);
    }

    return new HandleRef(this, NativeCode.getEmbeddedWidgetHandle());
}
我的问题是:

  • 轮询循环(或类似概念)是否有助于保持WPF的响应性
  • BuildWindowCore
    本身不能异步时,我们如何在GUI线程中干净地调用
    async Dispatcher.Yield()
    方法
  • 这是一个“确定”的解决方案,还是有更好的方法在调用
    HwndHost.BuildWindowCore()
    时不阻塞WPF GUI线程
您的WPF泵(调度程序)被阻塞。Async/await/Yield对您没有帮助,因为您不在异步函数中

当WPF位于BuildWindowCore()内时,您需要WPF继续泵送。您可以通过在不同的线程上调用本机代码来实现这一点,然后让您的WPF线程坐下来并泵送消息,直到工作线程完成。您可以使用

希望是这样的:

protected override HandleRef BuildWindowCore(HandleRef HWNDParent)
{
    // call into QT in worker thread
    var reParentTask = Task.Run(() => NativeCode.beginReParenting(HWNDParent.Handle));

    // pump messages while we wait for QT to do its stuff
    var frame = new DispatcherFrame();
    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
        new DispatcherOperationCallback(delegate (object f)
            {
                ((DispatcherFrame)f).Continue = !reParentTask.IsCompleted;
                return null;
            }),frame);

    Dispatcher.PushFrame(frame);

    return new HandleRef(this, NativeCode.getEmbeddedWidgetHandle());
}