3

我正在使用一个非托管函数,它需要一个指向一些非托管内存的指针。该函数在调用时立即返回,并在后台对内存进行异步操作。当操作完成时,它需要一个额外的 IntPtr 传递回我的托管代码。可以同时运行多个此类操作。

我将指向非托管内存的指针封装在自定义 SafeBuffer 实例中,我希望在异步操作完成时取回该实例。SafeBuffer 确保在没有对其的引用时正确释放内存。问题是,当非托管函数仍在使用内存时,当然不应该释放内存。

我怎样才能做到这一点?非托管函数被调用数十亿次,因此性能至关重要。

每当我调用该函数时,我都可以分配一个 GCHandle,在操作完成时使用它来取回 SafeBuffer,然后释放它。但是,分配句柄似乎很昂贵,并且性能会随着时间的推移而显着下降。

我可以分配一次 GCHandle,但是当内存未被非托管函数使用并且没有对 SafeBuffer 的引用时,非托管内存不会被释放。

有任何想法吗?

4

2 回答 2

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稳定的(不会更改)并且可以传递给非托管代码的任意值。

于 2013-07-17T19:40:33.763 回答
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 实例的引用,这会阻止 GCHandle 被释放,即使用户代码停止持有对 SafeMemory 实例的引用。

于 2013-07-18T01:06:31.933 回答