gpt4 book ai didi

c# - WPF : How to not block the GUI thread in HwndHost. BuildWindowCore 中的死锁?

转载 作者:行者123 更新时间:2023-12-03 06:52:13 34 4
gpt4 key购买 nike

我们有一个庞大的代码库,用于使用 Qt 在 C++ 中处理和可视化图像。现在用户想要在此基础上进行构建,但他们的代码库是带有 WPF 的 .NET Core 3.1。我们使用 PInvoke 来接口(interface)原生代码,并且可以非常成功地加入项目。少数原生小部件嵌入了 Win32-in-WPF-HwndHost wrapper 。一切都很好,我们对我们获得的里程感到非常高兴!
只有一个阻塞问题:HwndHost方法BuildWindowCore有时会陷入僵局。我们可以确定死锁的根本原因:
BuildWindowCore调用 Qt 将 native 小部件重新设置为托管小部件的句柄,我们使用阻塞调用来确保重新设置完成。然而,在重新设置小部件的父级期间,Qt 有时会调用 DefWindowProc将未处理的窗口消息传递回父 WPF 小部件。由于 WPF 线程被调用 Qt 阻塞,这是一个循环阻塞等待,以死锁结束。
虽然我可以理解这个问题,但我们对 WPF GUI 线程的了解还不够,无法解决这个问题。
到目前为止,我们尝试了什么:

  • 在后台调用 Qt(使用 await )但 BuildWindowCore不能是异步方法。
  • 将重新育儿移出BuildWindowCore , 并称之为 async稍后,但 HwndHost小部件需要在 BuildWindowCore 中进行重新设置,或者我们得到一个 WPF 错误,即 native 小部件句柄不是(还)WPF 小部件的子小部件。

  • 我有点不知所措。我们可以在 C++ 端非阻塞地调用 native 代码,并在 C# 中循环轮询它的完成情况。但是,当我们轮询 Qt 以重新设置小部件的父级时,我们如何将控制权交还给 WPF GUI 线程呢?
    最密切相关的 SO answer建议使用 await 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());
    }
    我的问题:
  • Dispatcher.Yield() (或类似的概念)在轮询循环中有助于保持 WPF 响应?
  • 我们怎样才能干净地调用 async Dispatcher.Yield() GUI 线程中的方法,当 BuildWindowCore本身不能异步吗?
  • 这是一个“好的”解决方案,还是有更好的方法在调用 HwndHost.BuildWindowCore() 时不阻塞 WPF GUI 线程? ?
  • 最佳答案

    您的 WPF 泵(调度程序)被阻塞。 Async/await/Yield 对您没有帮助,因为您不在异步功能中。
    当 WPF 位于 BuildWindowCore() 中时,您需要 WPF 保持抽水。您可以通过在不同的线程上调用您的 native 代码来做到这一点,然后让您的 WPF 线程坐下来并泵送消息,直到工作线程完成。您可以使用 Dispatcher.PushFrame .
    希望是这样的:

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

    关于c# - WPF : How to not block the GUI thread in HwndHost. BuildWindowCore 中的死锁?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63988620/

    34 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com