打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
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 …

抱歉

🙂

编辑2

我无法在Linux / Ubuntu / Mono上重现问题,尽管在相同条件下使用相同的代码(同时运行多个相同的可执行文件,发布模式等)

解决方法:

它只是代码中的一个错误:终结器不应该访问托管对象.

实现终结器的唯一原因是释放非托管资源.在这种情况下,您应该仔细实施the standard IDisposable pattern.

使用此模式,您可以实现受保护的方法“protected Dispose(bool disposing)”.从终结器调用此方法时,它会清除非托管资源,但不会尝试清理托管资源.

在您的示例中,您没有任何非托管资源,因此不应该实现终结器.

来源:https://www.icode9.com/content-1-322801.html
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
要谨慎使用Indy的TIdHashMessageDigest类
C#哈希Md5加密
Java Java 7 源码学习系列(一)——String
DotNet加密方式解析--散列加密
详述一条SQL引发的高CPU故障处理过程
关于ASP,ASP.NET,VB.NET里的MD5加密函数
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服