在C#中是否有一个boost :: shared_ptr ?

好奇的是,我一直在使用boost:shared_ptr过去很多 – 因为有多个对象存储一个指向单个对象的共享指针等。

C#中是否有相同的function?

boost :: shared_ptr允许在非垃圾收集的环境中引用计数指针。 .NET运行时允许完全垃圾收集,因此不需要引用计数指针容器 – 只需存储对象引用。

没必要。 .NET有一个垃圾收集器,一旦没有任何对象的引用,它将负责清理对象。

GC在内存管理方面不需要共享指针,但它不会对资源管理执行此操作。

IDisposable是.NET的资源管理解决方案,但它不允许共享所有权语义。 有关这些弱点的详细讨论,请参阅此文章 。

这是一个SharedRef的简单实现,它遵循堆分配的引用计数对象的boost :: shared_ptr模式。 不确定它有多么有用,但随意评论并改进它……

 ///  /// SharedRef class, which implements reference counted IDisposable ownership. /// See also the static helper class for an easier construction syntax. ///  public class SharedRef : IDisposable where T:class,IDisposable { private SharedRefCounter _t; ///  /// Create a SharedRef directly from an object. Only use this once per object. /// After that, create SharedRefs from previous SharedRefs. ///  ///  public SharedRef(T t) { _t = new SharedRefCounter(t); _t.Retain(); } ///  /// Create a SharedRef from a previous SharedRef, incrementing the reference count. ///  ///  public SharedRef(SharedRef o) { o._t.Retain(); _t = o._t; } public static SharedRef Create(T t) { return new SharedRef(t); } private bool _disposed = false; protected virtual void Dispose(bool disposing) { if (_disposed) return; if (disposing) { if (_t != null) { _t.Release(); _t = null; } } _disposed = true; } public void Dispose() { Dispose(true); } public T Get() { return _t.Get(); } } ///  /// Static helper class for easier construction syntax. ///  public static class SharedRef { ///  /// Create a SharedRef directly from an object. Only use this once per object. /// After that, create SharedRefs from previous SharedRefs. ///  ///  public static SharedRef Create(T t) where T : class,IDisposable { return new SharedRef(t); } ///  /// Create a SharedRef from a previous SharedRef, incrementing the reference count. ///  ///  public static SharedRef Create(SharedRef o) where T : class,IDisposable { return new SharedRef(o); } } ///  /// Class which holds the reference count for a shared object. ///  ///  internal class SharedRefCounter where T : class,IDisposable { private int _count; private readonly T _t; public T Get() { return _t; } public SharedRefCounter(T t) { _count = 0; _t = t; } ///  /// Decrement the reference count, Dispose target if reaches 0 ///  public void Release() { lock (_t) { if (--_count == 0) { _t.Dispose(); } } } ///  /// Increment the reference count ///  public void Retain() { lock (_t) { ++_count; } } } 

笔记:

  1. 要确保每个共享对象只有一个引用计数,请确保只直接从该对象创建1个SharedRef,然后从以前的SharedRefs创建新的SharedRef。 对于boost :: shared_ptr,这是相同的。 如果忘记这一点,为课程添加一些保护会很好。
  2. 令人遗憾的是,SharedRef本身必须是一个在堆上分配的引用类型,但我认为这是使它成为Disposable的唯一方法。
  3. 我还没有实现相当于weak_ptr的function,但我认为可以轻松添加。
  4. 它是线程安全的(我认为),但可能因为它使用锁而更有效。

这里有一些测试代码显示它在3个线程中的运行情况。

 [TestFixture] public class SharedRefTest { public class MyDisposable : IDisposable { private bool _disposed = false; private string _id; public MyDisposable(string id) { _id = id; } protected virtual void Dispose(bool disposing) { if (!_disposed) { if (disposing) { Console.WriteLine("{0}: Disposing {1}", Thread.CurrentThread.ManagedThreadId, _id); } _disposed = true; } } public void Dispose() { Dispose(true); } public override string ToString() { return _id; } } [Test] public void Run() { Task t1, t2, t3; // create 2 objects Console.WriteLine("{0}: starting initial scope", Thread.CurrentThread.ManagedThreadId); using (var o1 = SharedRef.Create(new MyDisposable("o1"))) using (var o2 = SharedRef.Create(new MyDisposable("o2"))) { // and 3 sharedrefs, which will be Disposed in 3 separate threads var p1 = SharedRef.Create(o1); var p2a = SharedRef.Create(o2); var p2b = SharedRef.Create(o2); t1 = Task.Run(() => { using (p1) { Console.WriteLine("{0}: in another thread, using o1", Thread.CurrentThread.ManagedThreadId); Thread.Sleep(1000); Console.WriteLine("{0}: in another thread, exiting scope", Thread.CurrentThread.ManagedThreadId); } }); t2 = Task.Run(() => { using (p2a) { Console.WriteLine("{0}: in another thread, using o2", Thread.CurrentThread.ManagedThreadId); Thread.Sleep(1000); Console.WriteLine("{0}: in another thread, exiting scope", Thread.CurrentThread.ManagedThreadId); } }); t3 = Task.Run(() => { using (p2b) { Console.WriteLine("{0}: in another thread, using o2", Thread.CurrentThread.ManagedThreadId); Thread.Sleep(1000); Console.WriteLine("{0}: in another thread, exiting scope", Thread.CurrentThread.ManagedThreadId); } }); Console.WriteLine("{0}: exiting initial scope", Thread.CurrentThread.ManagedThreadId); } t1.Wait(); t2.Wait(); t3.Wait(); } } 

阅读这篇文章了解更多信息:

  • 了解.NET中的垃圾收集

在不同的平台和编程语言中有几种自动内存管理策略。

两种主要方法适用于自动内存管理:引用计数和垃圾收集。 它们都值得研究,虽然第二个是更强大和普遍适用的。

(Bertran Meyer的面向对象软件构建,p.301)

引用计数(即shared_ptr)是实现自动内存管理的最简单方法之一。 它非常简单但有一些明显的缺点(它不能处理循环结构,并且它在时间和空间上都有性能开销。对于引用的每个操作,实现现在将执行算术运算 – 并且,在分离情况下,条件指令。此外,每个对象必须使用额外的字段进行扩展以保持计数。

第一种自动内存管理技术(引用计数)背后的想法很简单。 在每个对象中,我们保持对对象的引用数量的计数; 当该计数变为空时,该对象可以被回收。 该解决方案并不难实现(在语言实现级别)。 我们必须更新任何对象的引用计数,以响应可以创建对象的所有操作,附加对它的新引用并从中分离引用。

(Bertran Meyer的面向对象软件构建,p.301)

垃圾收集(巫婆在CLR中使用)基于两个主要属性:

健全性 :每个收集的对象都无法访问。

完整性 :将收集每个无法访问的对象。

垃圾收集基础

基本算法通常包括两个阶段,至少在概念上:标记和扫描。 标记阶段从原点开始,递归地跟随引用遍历结构的活动部分,标记为它遇到的所有对象都可达到。 扫描阶段遍历整个存储器结构,回收未标记的元素并取消标记所有内容。 与引用计数一样,对象必须包含一个额外的字段,此处用于标记; 但是空间开销可以忽略不计,因为每个对象一个就足够了。 正如我们研究动态绑定时所看到的,OO工具的实现要求除了与生成类的属性相对应的官方字段之外,每个对象还携带一些额外的内部信息(例如其类型)。 该信息通常占用每个对象一个或两个单词; 标记位通常可以挤入这些额外的单词之一,因此在实践中没有可观察到的开销。

PS有关CLR中垃圾收集的更多信息,请参阅Jeffrey Richter的第20章“通过C#CLR”

PSS .Net中的shared_ptr没有等价物,因为.Net使用垃圾收集进行自动内存管理,如果你想使用引用计数 – 你应该手动实现它(例如,用于资源管理)。