2

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

当前方案基于通过 IPC 与单独的本机 C++ 进程通信的 WCF 服务。

为了让事情更简单一点,这里有一个建议去混合模式(即有一个单一的.NET进程,它在其中加载本机C++层,很可能通过一个非常薄的C++/CLI层与其通信) . 主要关注的是垃圾收集或其他 .NET 方面是否会阻碍进程的非托管 C++ 部分的性能。

我开始查找安全点和 GC 辅助方法(例如 KeepAlive() 等)的概念,但我找不到任何关于此或基准的直接讨论。据我目前所知,安全点之一是如果线程正在执行未管理的代码,并且在这种情况下垃圾收集不会暂停任何线程(这是正确的吗?)来执行清理。

我想我的主要问题是在同一个进程中运行这两种类型的代码而不是拥有单独的进程时,本机端存在性能问题。

4

2 回答 2

2

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

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

确保编译器不会为某些标准 C++ 代码静默生成 MSIL(从而使其作为托管代码执行)有点棘手。但最终,您可以通过谨慎使用#pragma managed(push, off).

于 2013-02-21T18:02:47.713 回答
2

启动并运行混合模式应用程序非常容易,但要使其正常运行却非常困难。

我建议在选择该设计之前仔细考虑 - 特别是关于如何分层应用程序以及您期望非托管对象的生命周期。从过去的经验中的一些想法:

  1. C++ 对象生命周期 - 按体系结构。
    在本地范围内短暂使用 C++ 对象,然后立即处理它们。这听起来很明显但值得说明的是,C++ 对象是非托管资源,旨在用作非托管资源。通常,他们期望确定性的创建和销毁 - 通常会广泛使用 RAII。从托管程序控制这可能非常尴尬。IDispose 模式的存在就是为了尝试解决这个问题。这对于短寿命的对象可以很好地工作,但对于长寿命的对象来说相当乏味且难以正确处理。特别是如果您开始使非托管对象成为托管类的成员,而不是仅存在于函数范围内的东西,那么很快程序中的每个类都必须是 IDisposable 并且突然之间托管编程变得比非托管编程更难。

  2. GC 太激进了。永远值得记住的是,当我们谈论托管对象超出范围时,我们的意思是在 IL 编译器/运行时的眼中,而不是您正在阅读代码的语言。如果非托管对象作为成员和托管对象保留旨在删除它事情可能会变得复杂。如果您的程序从上到下的处置模式不完整,则 GC 可能会变得相当激进。例如,假设您尝试编写一个托管类,该类在其终结器中删除一个非托管对象。假设您对托管对象所做的最后一件事是访问非托管指针以调用方法。然后 GC 可能会决定在该非托管调用期间是收集托管对象的好时机。突然,您的非托管指针在方法调用中被删除。

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

  4. 缓冲区复制。您还需要考虑在哪里分配任何大内存块。托管块可以固定为看起来像非托管块。非托管块只有在需要看起来像托管块时才能被复制。但是,这个大型托管块何时会真正被释放?

于 2013-02-21T19:03:59.580 回答