代码之家  ›  专栏  ›  技术社区  ›  xenn_33

完成集合字典<object,IntPtr>

  •  3
  • xenn_33  · 技术社区  · 16 年前

    我有一个班级,有一个 Dictionary<object, IntPtr> .

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

    然后我将在另一个线程中使用该内存。实际上,在做了一些工作之后,线程通过 Marshal.FreeHGlobal

    如何完成整个集合(以防某个线程崩溃,而我的内存仍处于分配状态)?

    IntPtr SafeHandle . 这个有用吗?

    3 回复  |  直到 16 年前
        1
  •  1
  •   Hans Passant    16 年前

    为IntPtr编写包装器当然可以。但是,如果只要该项存储在字典中,内存就应该保持有效,这是没有必要的。如果是这样的话,那么如果您创建自己的目录(当从字典中删除某个项时,该目录会自动释放内存),那么在客户端代码上就容易多了。客户机不需要为每个项调用Dispose(),这总是一个优势。

    为此,从 IDictionary<object, IntPtr> 可识别的。您可以简单地将大多数方法调用委托给私有字典。您需要一个自定义的Add()来分配内存,Delete来释放内存。并实现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(); }
    }
    

    AllocTaskMem是更好的分配器,顺便说一句,它没有遗留的包袱。

        2
  •  1
  •   Will Dean    16 年前

    最好将完整的Dispose/Finalizer模式添加到包含集合的类中,并让它遍历字典和清理。

    或者为HGlobal编写带有终结器的包装器。

    更新:这可能有用- http://social.msdn.microsoft.com/forums/en-US/csharpgeneral/thread/f74b7c3c-12c4-466b-9754-82e9dea8b83e

        3
  •  1
  •   Chris Taylor    16 年前

    一种方法是将线程代码放在try/finally块中,让finally块释放内存,这样如果线程崩溃(我想你的意思是抛出异常?),finally块将确保内存被释放。

    如果您在Dispose方法中释放内存,则自定义SafeHandle会有所帮助,但要使其正常工作,您需要在线程崩溃之前从字典中删除该项,以便不再通过字典引用SafeHandle。否则,只有当字典被释放时,内存才会被释放,这取决于你对字典本身的终生管理。

    WeakReference ,不过,如果可以的话,我建议将SafeHandle与try/finally结合使用。