好奇,我一直在使用boost:shared_ptr过去很多 - 因为有多个对象存储一个指向单个对象的共享指针等。
C#中是否有相同的功能?
答案 0 :(得分:9)
boost :: shared_ptr允许在非垃圾收集的环境中引用计数指针。 .NET运行时允许完全垃圾收集,因此不需要引用计数指针容器 - 只需存储对象引用。
答案 1 :(得分:5)
没有必要。 .NET有一个垃圾收集器,一旦没有任何对象的引用,它将负责清理对象。
答案 2 :(得分:5)
GC在内存管理方面不需要共享指针,但它不会对资源管理执行此操作。
IDisposable是.NET的资源管理解决方案,但它不允许共享所有权语义。有关弱点的详细讨论,请参见this article。
这是SharedRef的一个简单实现,它遵循堆分配的引用计数对象的boost :: shared_ptr模式。 不确定它有多么有用,但随意评论和改进它......
/// <summary>
/// SharedRef class, which implements reference counted IDisposable ownership.
/// See also the static helper class for an easier construction syntax.
/// </summary>
public class SharedRef<T> : IDisposable
where T:class,IDisposable
{
private SharedRefCounter<T> _t;
/// <summary>
/// Create a SharedRef directly from an object. Only use this once per object.
/// After that, create SharedRefs from previous SharedRefs.
/// </summary>
/// <param name="t"></param>
public SharedRef(T t)
{
_t = new SharedRefCounter<T>(t);
_t.Retain();
}
/// <summary>
/// Create a SharedRef from a previous SharedRef, incrementing the reference count.
/// </summary>
/// <param name="o"></param>
public SharedRef(SharedRef<T> o)
{
o._t.Retain();
_t = o._t;
}
public static SharedRef<T> Create(T t)
{
return new SharedRef<T>(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();
}
}
/// <summary>
/// Static helper class for easier construction syntax.
/// </summary>
public static class SharedRef
{
/// <summary>
/// Create a SharedRef directly from an object. Only use this once per object.
/// After that, create SharedRefs from previous SharedRefs.
/// </summary>
/// <param name="t"></param>
public static SharedRef<T> Create<T>(T t) where T : class,IDisposable
{
return new SharedRef<T>(t);
}
/// <summary>
/// Create a SharedRef from a previous SharedRef, incrementing the reference count.
/// </summary>
/// <param name="o"></param>
public static SharedRef<T> Create<T>(SharedRef<T> o) where T : class,IDisposable
{
return new SharedRef<T>(o);
}
}
/// <summary>
/// Class which holds the reference count for a shared object.
/// </summary>
/// <typeparam name="T"></typeparam>
internal class SharedRefCounter<T> where T : class,IDisposable
{
private int _count;
private readonly T _t;
public T Get()
{
return _t;
}
public SharedRefCounter(T t)
{
_count = 0;
_t = t;
}
/// <summary>
/// Decrement the reference count, Dispose target if reaches 0
/// </summary>
public void Release()
{
lock (_t)
{
if (--_count == 0)
{
_t.Dispose();
}
}
}
/// <summary>
/// Increment the reference count
/// </summary>
public void Retain()
{
lock (_t)
{
++_count;
}
}
}
注意:
这是一些测试代码,显示它在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();
}
}
答案 3 :(得分:1)
阅读这篇文章了解更多信息:
答案 4 :(得分:0)
在不同的平台和编程语言中有几种自动内存管理策略。
两种主要方法适用于自动内存管理:参考 计数和垃圾收集。它们都值得研究,尽管第二个 到目前为止,它更强大,更普遍适用。
(Bertran Meyer的面向对象软件构建,p.301)
引用计数(即shared_ptr)是实现自动内存管理的最简单方法之一。它非常简单但有一些明显的缺点(它不能处理循环结构,并且它在时间和空间上都有性能开销。对于引用的每个操作,实现现在将执行算术运算 - 并且,在分离的情况下,条件指令。此外,每个对象必须使用额外的字段进行扩展以保存计数。
第一种自动内存管理技术背后的思想,参考计数, 很简单。在每个对象中,我们保持对对象的引用数量的计数;什么时候 该计数变为空,该对象可以被回收。 该解决方案并不难实现(在语言实现级别)。我们 必须更新任何对象的引用计数以响应可以创建的所有操作 对象,附加一个新的引用并从中分离引用。
(Bertran Meyer的面向对象软件构建,p.301)
垃圾收集(在CLR中使用)基于两个主要属性:
健全性:每个收集的对象都无法访问。
完整性:将收集每个无法访问的对象。
垃圾收集基础
基本算法通常包括两个阶段,至少在概念上:标记和 扫。标记阶段,从起源开始,递归地跟随参考 遍历结构的活动部分,将其遇到的所有对象标记为可达。 扫描阶段遍历整个存储器结构,回收未标记的元素 一切都没有标记。 与引用计数一样,对象必须包含一个额外的字段,此处用于表示 标记;但是空间开销可以忽略不计,因为每个对象一个就足够了。正如我们研究动态绑定时所看到的,O-O设施的实现需要每一个 对象除了官方之外还携带一些额外的内部信息(例如其类型) 对应于生成类的属性的字段。这个信息通常 每个对象占一个或两个单词;标记位通常可以挤入其中一个 这些额外的词,所以在实践中没有可观察到的开销。
P.S。有关CLR中垃圾收集的更多信息,请参阅Jeffrey Richter的第20章“通过C#CLR”
P.S.S。 .Net中的shared_ptr没有等价物,因为.Net使用垃圾收集进行自动内存管理,如果你想使用引用计数 - 你应该手动实现它(例如,用于资源管理)。