c# – Finalizer在其对象仍在使用时启动
作者:互联网
简介:C#/ .NET应该是垃圾回收. C#有一个析构函数,用于清理资源.当一个对象A被垃圾收集在同一行我试图克隆其变量成员之一时会发生什么?显然,在多处理器上,有时,垃圾收集器赢了……
问题
今天,在关于C#的培训课程中,老师向我们展示了一些仅在多处理器上运行时才包含错误的代码.
我将总结一下,有时候,编译器或JIT通过在从被调用方法返回之前调用C#类对象的终结器来搞砸.
Visual C 2005文档中给出的完整代码将作为“答案”发布,以避免提出非常大的问题,但必要的内容如下:
以下类具有“Hash”属性,该属性将返回内部数组的克隆副本.在构造中,数组的第一项值为2.在析构函数中,其值设置为零.
关键是:如果你试图得到“示例”的“哈希”属性,你将获得一个干净的数组副本,其第一个项目仍然是2,因为正在使用该对象(因此,不是垃圾收集/定稿):
public class Example
{
private int nValue;
public int N { get { return nValue; } }
// The Hash property is slower because it clones an array. When
// KeepAlive is not used, the finalizer sometimes runs before
// the Hash property value is read.
private byte[] hashValue;
public byte[] Hash { get { return (byte[])hashValue.Clone(); } }
public Example()
{
nValue = 2;
hashValue = new byte[20];
hashValue[0] = 2;
}
~Example()
{
nValue = 0;
if (hashValue != null)
{
Array.Clear(hashValue, 0, hashValue.Length);
}
}
}
但没有什么是这么简单……
使用这个类的代码是在一个线程内部进行的,当然,对于测试,该应用程序是多线程的:
public static void Main(string[] args)
{
Thread t = new Thread(new ThreadStart(ThreadProc));
t.Start();
t.Join();
}
private static void ThreadProc()
{
// running is a boolean which is always true until
// the user press ENTER
while (running) DoWork();
}
DoWork静态方法是问题发生的代码:
private static void DoWork()
{
Example ex = new Example();
byte[] res = ex.Hash; // [1]
// If the finalizer runs before the call to the Hash
// property completes, the hashValue array might be
// cleared before the property value is read. The
// following test detects that.
if (res[0] != 2)
{
// Oops... The finalizer of ex was launched before
// the Hash method/property completed
}
}
每隔1,000,000个DoWork提供一次,显然,垃圾收集器会发挥其魔力,并试图回收“ex”,因为它不再在函数的重新编写代码中引用,而这次,它比“哈希”更快得到方法.所以我们最终得到的是一个零ed字节数组的克隆,而不是正确的一个(第一项为2).
我的猜测是代码的内联,它基本上取代了DoWork函数中标记为[1]的行:
// Supposed inlined processing
byte[] res2 = ex.Hash2;
// note that after this line, "ex" could be garbage collected,
// but not res2
byte[] res = (byte[])res2.Clone();
如果我们认为Hash2是一个简单的访问器,编码如下:
// Hash2 code:
public byte[] Hash2 { get { return (byte[])hashValue; } }
所以,问题是:这应该在C#/ .NET中以这种方式工作,还是可以认为这是JIT编译器的错误?
编辑
有关解释,请参阅Chris Brumme和Chris Lyons的博客.
http://blogs.msdn.com/cbrumme/archive/2003/04/19/51365.aspx
http://blogs.msdn.com/clyon/archive/2004/09/21/232445.aspx
每个人的回答都很有趣,但我不能选择一个比另一个好.所以我给了你一个1 …
抱歉
标签:c,net,garbage-collection,finalization 来源: https://codeday.me/bug/20190713/1448791.html