我的课程的字段为Dictionary<object, IntPtr>
。
当用户调用我的类的某个方法时,我会动态分配内存:
IntPtr somePointer = Marshal.AllocHGlobal(/*some desired size*/);
然后我将在另一个线程中使用该内存。实际上,在做了一些工作后,该线程通过Marshal.FreeHGlobal
释放分配的内存并从集合中删除相应的密钥。但是这个线程有可能崩溃,所以我正在考虑正确的终结。
如何最终确定整个集合(如果某个线程崩溃且我的内存仍然分配)?
我倾向于将IntPtr
更改为SafeHandle
。这会有帮助吗?
答案 0 :(得分:1)
一个选项是将你的线程代码放在try / finally块中并让finally块释放内存,这样如果线程崩溃(我假设你的意思是抛出异常?)finally块将确保内存被释放了。
如果您在Dispose方法中释放内存,自定义SafeHandle会有所帮助,但为了使其工作,您需要在线程崩溃之前从字典中删除该项,以便不再引用SafeHandle通过字典。否则,只有在发布字典后才会释放内存,这取决于您自己对字典的生命周期管理。
或者您可以考虑使用WeakReference查看更具异国情调的解决方案,但如果可以,我会建议将SafeHandle与try / finally结合使用。
答案 1 :(得分:1)
我认为你会发现SafeHandle在清理时只调用CloseHandle,我认为这与FreeHGlobal不同。
您可能最好将完整的Dispose / Finalizer模式添加到包含该集合的类中,并让它遍历字典并进行清理。
或者为HGlobal写一个带有终结器的包装器。
更新:这可能很有用 - http://social.msdn.microsoft.com/forums/en-US/csharpgeneral/thread/f74b7c3c-12c4-466b-9754-82e9dea8b83e
答案 2 :(得分:1)
为IntPtr编写包装器当然可以工作。但是,只要项目存储在字典中,内存就不应该保持有效。如果是这种情况,如果您创建自己的目录,在客户端代码上更容易,当从字典中删除项目时,该目录会自动释放内存。客户无需为每个项目调用Dispose(),这一直是一个优势。
为此,从IDictionary<object, IntPtr>
和IDisposable派生自己的类。您可以简单地将大多数方法调用委托给私有字典。你需要一个自定义的Add()来分配内存,删除来释放它。并实现Dispose()和终结器进行清理。但代码有点难看:
class MyDictionary : IDictionary<object, IntPtr>, IDisposable {
private Dictionary<object, IntPtr> impl = new Dictionary<object, IntPtr>();
public void Add(object key) {
IntPtr mem = Marshal.AllocCoTaskMem(666); // Something smarter here...
impl.Add(key, mem);
}
public bool Remove(object key) {
if (!impl.ContainsKey(key)) return false;
Marshal.FreeCoTaskMem(impl[key]);
return impl.Remove(key);
}
protected void Dispose(bool disposing) {
foreach (IntPtr mem in impl.Values) Marshal.FreeCoTaskMem(mem);
if (disposing) impl.Clear();
}
public void Dispose() {
Dispose(true);
}
~MyDictionary() {
Dispose(false);
}
// Boilerplate
public void Add(object key, IntPtr value) { throw new NotImplementedException(); }
public void Add(KeyValuePair<object, IntPtr> item) { throw new NotImplementedException(); }
public bool Remove(KeyValuePair<object, IntPtr> item) { throw new NotImplementedException(); }
public bool ContainsKey(object key) { return impl.ContainsKey(key); }
public ICollection<object> Keys { get { return impl.Keys; }}
public bool TryGetValue(object key, out IntPtr value) { return impl.TryGetValue(key, out value); }
public ICollection<IntPtr> Values { get {return impl.Values; }}
public IntPtr this[object key] { get { return impl[key]; } set { impl[key] = value; } }
public void Clear() { impl.Clear(); }
public bool Contains(KeyValuePair<object, IntPtr> item) { return impl.Contains(item); }
public void CopyTo(KeyValuePair<object, IntPtr>[] array, int arrayIndex) { (impl as ICollection<KeyValuePair<object, IntPtr>>).CopyTo(array, arrayIndex); }
public int Count { get { return impl.Count; }}
public bool IsReadOnly { get { return (impl as ICollection<KeyValuePair<object, IntPtr>>).IsReadOnly; } }
public IEnumerator<KeyValuePair<object, IntPtr>> GetEnumerator() { return impl.GetEnumerator(); }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return (impl as System.Collections.IEnumerable).GetEnumerator(); }
}
AllocCoTaskMem是更好的分配器btw,它没有传统的行李。