我正在使用一个非托管函数,它接受指向某些非托管内存的指针。该函数在被调用时立即返回,并在后台对内存进行异步操作。它需要一个额外的IntPtr,它在操作完成时传递回我的托管代码。可以同时运行多个此类操作。
我将指向非托管内存的指针封装在自定义SafeBuffer实例中,我希望在异步操作完成时返回该实例。 SafeBuffer确保在没有引用时正确释放内存。问题是当然,在非托管函数仍然使用内存时,不应释放内存。
我怎样才能做到这一点?非托管函数被称为数十亿次,因此性能至关重要。
每当我调用该函数时,我都可以分配一个GCHandle,在操作完成时使用它来获取SafeBuffer,并释放它。但是,分配句柄似乎很昂贵,而且性能会随着时间的推移而显着下降。
我可以分配一次GCHandle,但是当非托管函数没有使用内存并且没有对SafeBuffer的引用时,非托管内存不会被释放。
有什么想法吗?
答案 0 :(得分:0)
您可以在管理方保留查找表:
static ConcurrentDictionary<long, SafeBuffer> handles = new ...();
static long nextHandle = 0;
static long GetNextHandle(SafeBuffer buffer) {
var handle = Interlocked.Increment(ref nextHandle);
handles.Add(handle, buffer);
return handle;
}
在调用非托管代码之前调用GetNextHandle
。完成后,从handles
字典中删除句柄。
句柄只是一个任意long
,它是稳定的(不会改变)并且可以传递给非托管代码。
答案 1 :(得分:0)
计算机科学中的所有问题都可以通过另一层次的间接解决:
public class SafeMemory : SafeBuffer
{
private GCHandle referenceHandle;
private SafeMemory[] reference;
public SafeMemory()
{
this.reference = new SafeMemory[1];
this.referenceHandle = GCHandle.Alloc(this.reference);
}
~SafeMemory()
{
this.referenceHandle.Free();
}
public void Start()
{
this.reference[0] = this;
StartUnmanagedAsyncOperation(this, this.referenceHandle);
}
static void AsyncOperationCompleted(GCHandle referenceHandle)
{
((SafeMemory[])referenceHandle.Target)[0] = null;
}
}
(为简洁起见,省略了SafeBuffer实现。)
用户代码创建并保存对SafeMemory实例的引用。 SafeMemory实例和GCHandle保存对1元素SafeMemory数组的引用。当用户代码不再保存对SafeMemory实例的引用时,将释放GCHandle。
在进行非托管异步操作时,会使该数组保存对SafeMemory实例的引用。这意味着GCHandle拥有对数组的引用,并且该数组包含对SafeMemory实例的引用,即使用户代码停止持有对SafeMemory实例的引用,也会阻止GCHandle被释放。