完成集合Dictionary <object,intptr =“”> </object,>

时间:2010-07-17 19:19:51

标签: c# .net

我的课程的字段为Dictionary<object, IntPtr>

当用户调用我的类的某个方法时,我会动态分配内存:

IntPtr somePointer = Marshal.AllocHGlobal(/*some desired size*/);

然后我将在另一个线程中使用该内存。实际上,在做了一些工作后,该线程通过Marshal.FreeHGlobal释放分配的内存并从集合中删除相应的密钥。但是这个线程有可能崩溃,所以我正在考虑正确的终结。

如何最终确定整个集合(如果某个线程崩溃且我的内存仍然分配)?

我倾向于将IntPtr更改为SafeHandle。这会有帮助吗?

3 个答案:

答案 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,它没有传统的行李。