多线程应用程序中静态全局非托管资源的管理
我们遇到了一个资源管理问题,我们已经困扰了几个星期,虽然我们终于找到了解决方案,但对我来说仍然很奇怪。
我们有大量针对遗留系统开发的互操作代码,这些代码公开了 C API。该系统的众多特性之一是(出于未知原因),似乎属于进程范围的“环境”必须在使用 API 之前进行初始化。但是,它只能初始化一次,并且在完成后必须“关闭”。
我们最初使用单例模式来完成此任务,但当我们在 IIS 托管的 Web 服务中使用该系统时,我们的 AppDomain 偶尔会被回收,从而导致内存泄漏的“孤立”环境。由于最终确定和(显然)甚至 IIS 回收都是不确定的,并且在所有情况下都难以检测,因此我们已切换到似乎运行良好的处置+引用计数模式。然而,手动进行引用计数感觉很奇怪,我确信有更好的方法。
对于在这样的环境中管理静态全球一次性资源有什么想法吗?
这是环境管理的大致结构:
public class FooEnvironment : IDisposable
{
private bool _disposed;
private static volatile int _referenceCount;
private static readonly object InitializationLock = new object();
public FooEnvironment()
{
lock(InitilizationLock)
{
if(_referenceCount == 0)
{
SafeNativeMethods.InitFoo();
_referenceCount++;
}
}
}
public void Dispose()
{
if(_disposed)
return;
lock(InitilizationLock)
{
_referenceCount--;
if(_referenceCount == 0)
{
SafeNativeMethods.TermFoo();
}
}
_disposed = true;
}
}
public class FooItem
{
public void DoSomething()
{
using(new FooEnvironment())
{
// environment is now initialized (count == 1)
NativeMethods.DoSomething();
// superfluous here but for our purposes...
using(new FooEnvironment())
{
// environment is initialized (count == 2)
NativeMethods.DoSomethingElse();
}
// environment is initialized (count == 1)
}
// environment is unloaded
}
}
We've run into a resource management problem that we've been struggling with for several weeks now and while we finally have a solution, it still seems weird to me.
We have a significant amount of interop code we've developed against a legacy system, which exposes a C API. One of the many peculiarities of this system is that (for reasons unknown), the "environment", which appears to be process-scoped must be initialized prior to the API being consumed. However, it can only be initialized once and must be "shutdown" once you're finished with it.
We were originally using a singleton pattern to accomplish this but as we're consuming this system inside an IIS hosted web service, our AppDomain will occasionally be recycled, leading to "orphaned" environments that leak memory. Since finalization and (apparently) even IIS-recycling is non-deterministic and hard to detect in all cases, we've switched to a disposal+ref counting pattern that seems to work well. However, doing reference counting manually feels weird and I'm sure there's a better approach.
Any thoughts on managing a static global disposable resource in an environment like this?
Here's the rough structure of the environment management:
public class FooEnvironment : IDisposable
{
private bool _disposed;
private static volatile int _referenceCount;
private static readonly object InitializationLock = new object();
public FooEnvironment()
{
lock(InitilizationLock)
{
if(_referenceCount == 0)
{
SafeNativeMethods.InitFoo();
_referenceCount++;
}
}
}
public void Dispose()
{
if(_disposed)
return;
lock(InitilizationLock)
{
_referenceCount--;
if(_referenceCount == 0)
{
SafeNativeMethods.TermFoo();
}
}
_disposed = true;
}
}
public class FooItem
{
public void DoSomething()
{
using(new FooEnvironment())
{
// environment is now initialized (count == 1)
NativeMethods.DoSomething();
// superfluous here but for our purposes...
using(new FooEnvironment())
{
// environment is initialized (count == 2)
NativeMethods.DoSomethingElse();
}
// environment is initialized (count == 1)
}
// environment is unloaded
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我首先在这里跳起来,因为关于您的特定代码库有很多未知因素,但我想知道基于会话的方法是否有任何进展?您可以拥有一个(线程安全)会话工厂单例,它负责确保仅初始化一个环境,并且通过将其绑定到 ASP.NET AppDomain 和/或类似事件上的事件来适当地处置该环境。您需要将此会话模型烘焙到 API 中,以便所有客户端在进行任何调用之前首先建立会话。对于这个答案的模糊性表示歉意。如果您可以提供一些示例代码,也许我可以给出更具体/详细的答案。
I'm jumping in feet first here as there are a lot of unknowns about you particular code base, but I'm wondering is there is any mileage in a session based approach? You could have a (thread safe) session factory singleton that is responsible for ensuring only one environment is initialised and that environment is disposed appropriately by binding it to events on the ASP.NET AppDomain and/or similar. You would need to bake this session model into your API so that all client first established a session before making any calls. Apologies for the vagueness of this answer. If you can provide some example code perhaps I could give a more specific/detail answer.
您可能需要考虑的一种方法是为非托管组件创建一个独立的 AppDomain。这样,当 IIS 托管的 AppDomain 被回收时,它就不会成为孤立的。
One approach you might want to consider is to create an isolated AppDomain for your unmanaged component. In this way it won't be orphaned when an IIS-hosted AppDomain is recycled.