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
中进行重新建立父子关系,否则我们会收到一个WPF错误,即本机小部件句柄不是(尚未)WPF小部件的子小部件BuildWindowCore
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的响应性
- 当
本身不能异步时,我们如何在GUI线程中干净地调用BuildWindowCore
方法async Dispatcher.Yield()
- 这是一个“确定”的解决方案,还是有更好的方法在调用
时不阻塞WPF GUI线程HwndHost.BuildWindowCore()
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());
}