C++ 混合模式进程与托管到非托管IPC

C++ 混合模式进程与托管到非托管IPC,c++,.net,garbage-collection,c++-cli,ipc,C++,.net,Garbage Collection,C++ Cli,Ipc,我正在努力为我正在从事的一个当前项目提出设计候选方案。它的客户端接口基于WCF服务,公开公共方法和回调。请求一直被路由到执行计算、操作等的C++库( 当前的方案是基于WCF服务通过独立的IPC来与单独的本地C++进程对话的。 为了使事情变得简单一点,这里有一个建议,可以混合模式(即,有一个单一的.NET进程,它在其中加载本地C++层,最有可能通过非常薄的C++/CLI层与它通信)。主要关注的是垃圾回收或其他.NET方面是否会阻碍进程的非托管C++部分的性能。 我开始查找安全点和GC助手方法的概念

我正在努力为我正在从事的一个当前项目提出设计候选方案。它的客户端接口基于WCF服务,公开公共方法和回调。请求一直被路由到执行计算、操作等的C++库(

当前的方案是基于WCF服务通过独立的IPC来与单独的本地C++进程对话的。 <>为了使事情变得简单一点,这里有一个建议,可以混合模式(即,有一个单一的.NET进程,它在其中加载本地C++层,最有可能通过非常薄的C++/CLI层与它通信)。主要关注的是垃圾回收或其他.NET方面是否会阻碍进程的非托管C++部分的性能。 我开始查找安全点和GC助手方法的概念(例如KeepAlive(),等等),但我找不到任何关于这个或基准的直接讨论。据我目前所知,其中一个安全点是,如果一个线程正在执行未更改的代码,在这种情况下,垃圾收集不会挂起任何线程(这是否正确?)来执行清理


我想我的主要问题是,当在同一进程中运行这两种类型的代码时,与使用单独的进程相比,本机端存在性能问题。

如果有一个线程从未执行过任何托管代码,它将不会在.NET垃圾回收期间冻结

如果使用托管代码的线程当前正在本机代码中运行,则垃圾回收器不会冻结该线程,而是在下次到达托管代码时将该线程标记为停止。但是,如果您正在考虑一个长时间不返回的本机分派循环,您可能会发现您正在阻塞垃圾收集器(或者将东西固定住,导致GC和碎片速度变慢)。因此,我建议让线程在本机代码中执行重要任务时保持完全纯净


确保编译器不为某些标准C++代码默默地生成MSIL(从而使其作为托管代码执行)有点棘手。但最终,您可以通过仔细使用

#pragma managed(push,off)
来实现这一点。如果您有一个线程从未执行过任何托管代码,它将不会在.NET垃圾收集期间被冻结

如果使用托管代码的线程当前正在本机代码中运行,则垃圾回收器不会冻结该线程,而是在下次到达托管代码时将该线程标记为停止。但是,如果您正在考虑一个长时间不返回的本机分派循环,您可能会发现您正在阻塞垃圾收集器(或者将东西固定住,导致GC和碎片速度变慢)。因此,我建议让线程在本机代码中执行重要任务时保持完全纯净


确保编译器不为某些标准C++代码默默地生成MSIL(从而使其作为托管代码执行)有点棘手。但最终,您可以通过小心地使用pragma managed(push,off)来实现这一点。启动并运行混合模式应用程序非常容易,但要使其正常工作可能非常困难

我建议在选择这种设计之前仔细考虑——特别是如何分层应用程序以及非托管对象的预期寿命。从过去的经验中得出的一些想法:

  • C++对象生命周期-按体系结构划分。
    在本地范围内简单地使用C++对象,然后立即处理它们。 这听起来很明显,但值得说明的是,C++对象是非托管资源,它们被设计成非托管资源。通常,他们期望确定性的创造和破坏——通常广泛使用RAII。从托管程序进行控制可能会非常棘手。IDispose模式的存在就是为了解决这个问题。这对于寿命短的对象很有效,但对于寿命长的对象来说相当繁琐,很难正确处理。特别是,如果您开始使非托管对象成为托管类的成员,而不是只存在于函数范围内的对象,那么很快程序中的每个类都必须是IDisposable的,并且托管编程突然变得比ummanaged编程更难

  • GC太激进了。 始终值得记住的是,当我们谈论超出范围的托管对象时,我们的意思是在IL编译器/运行时的眼中,而不是在阅读代码的语言中。如果一个ummanaged对象作为成员保留,并且一个托管对象被设计为删除它,那么事情可能会变得复杂。如果您的dispose模式从程序的顶部到底部都不完整,那么GC可能会变得相当激进。例如,假设您尝试编写一个托管类,该类删除其finalizer中的非托管对象。假设您对托管对象所做的最后一件事是访问非托管指针以调用方法。然后GC可能会决定在非托管调用期间收集托管对象是一个很好的时机。在方法调用过程中,非托管指针突然被删除

  • GC的攻击性不够强。 如果您在地址限制范围内工作(例如,您需要一个32位版本),那么您需要记住,GC保留内存,除非它认为需要释放。它对这些思想的唯一输入是管理世界。如果非托管分配器需要空间,则与GC没有连接。非托管分配可能会失败,因为GC没有收集长期超出范围的对象。有一个内存压力API,但它只对非常简单的设计有用

  • 缓冲区复制。您还需要考虑在哪里分配任何大内存块。托管块可以固定为类似于非托管块。非托管块只有在需要看起来像托管块时才能被复制。然而,什么时候会有这么大的管理