我正在编写一个在多个线程中调用多个基于 C 的函数(p/Invoke)的程序。
有时,程序会因访问冲突错误而崩溃。我的第一个想法是 GC 优化了内存并将 C 函数正在处理的内存块移动到不同的位置。
我想做的是让 GC 工作,但禁用它移动(碎片整理)内存的部分。
有没有办法做到这一点?
我正在编写一个在多个线程中调用多个基于 C 的函数(p/Invoke)的程序。
有时,程序会因访问冲突错误而崩溃。我的第一个想法是 GC 优化了内存并将 C 函数正在处理的内存块移动到不同的位置。
我想做的是让 GC 工作,但禁用它移动(碎片整理)内存的部分。
有没有办法做到这一点?
正如其他答案所说,首先要做的是确保您正确固定对象。假设你已经这样做了,还有什么问题?
class C
{
public int handle;
...
~C() { InteropLibrary.DestroyHandle(handle); }
}
void M()
{
C c = GetSomeObjectUsefulInUnmanagedCode();
D d = InteropLibrary.UnmanagedMethodThatUsesHandle(c);
// COMMENT
d.DoSomethingWithStoredHandle();
}
如果在COMMENT
(*) 处发生垃圾收集怎么办?垃圾收集器可以自由地说“嘿,c
这个方法中再也不会引用局部变量;我可以咄咄逼人,把它当作死了!”。如果终结器运行并且句柄被销毁,那么当最后一个方法运行时,它访问一个被销毁的句柄并崩溃。
为了解决这个罕见但可能出现的问题,您可以GC.KeepAlive
告诉垃圾收集器在清理特定引用时不那么激进。如果你一直c
存活到方法结束,那么你知道它的析构函数不可能运行。
(*) 当然 GC 运行在不同的线程上,并且可以随时运行。GC 可中断和不可中断的操作细节很复杂,您不应依赖这些实现细节来确保正确性。
fixed
在大多数情况下,您可以使用关键字。
从 Eric Lippert 的新博客看来,至少还有另外两种可能性:
请注意,在这两个选项中,您需要确保正确释放内存。
附带说明一下,如果您的问题是移动了一小部分内存(导致访问冲突),那么解决方案几乎永远不会禁用整个移动内存部分。
我不认为您可以全局执行此操作,但您可以使用fixed
关键字来固定特定对象,以获得所需的效果。