代码之家  ›  专栏  ›  技术社区  ›  Paulo Santos

将对象字段分配给局部变量有什么好处?

  •  3
  • Paulo Santos  · 技术社区  · 15 年前

    我正在尝试为我的个人项目开发一些异步方法,我正在研究这个框架以供参考。

    我已经下载了 .NET source code 更仔细地看一下这些钻头和螺栓(有开发人员的评论,反光镜没有告诉我们的是-p)

    总之,在许多.NET类中,我遇到了以下模式:

    class SomeType
    {
      // ...
      SomeClass m_Field;
      // ...
      SomeClass SomeMethod()
      { 
        SomeClass localField = m_Field;
        if (localField == null)
        {
          localField = new SomeClass(); 
          m_Field = localField;
        } 
        return localField; 
      }
    }
    

    这让我想知道使用这种模式有什么好处?

    据我所知,从性能上看,上面的模式比下面的模式更糟:

    class SomeType
    {
      // ...
      SomeClass m_Field;
      // ...
      SomeClass SomeMethod()
      { 
        if (m_Field == null)
        {
          m_Field = new SomeClass(); 
        } 
        return m_Field; 
      }
    }
    

    还是我在这里遗漏了什么?

    4 回复  |  直到 15 年前
        1
  •  2
  •   Nick Guerrera    15 年前

    在许多情况下,这种差异纯粹是审美上的和主观上的,但是想到一个与另一个的原因有三个:

    1. 螺纹安全: 无锁算法可能需要担心这个问题,但是如果所有同步都是通过锁来完成的,那就不应该是个问题。

    2. 性能: 可以 在某些情况下要稍微快一点,但我真的怀疑这在大多数情况下会有不同。

    3. 异常安全: 通常,您需要小心地将中间更改放入局部变量,然后只在操作完成后将结果发布到字段,而不引发异常。这充当了一种事务机制,因为没有人会看到只设置了一半字段的对象。

        2
  •  2
  •   ironic    15 年前

    当您从一个线程调用某个方法时,这种方法可能有助于保护您不受这种情况的影响,但是在您检查m_字段是否为空后,控件将传递给另一个线程,该线程将其设置为空。然后控件返回到第一个线程,但它仍然认为m_字段!=null,这可能会导致nullreferenceexception

    据我所知,在里希特的“CLR Via C”一章中有几个关于它的词。

        3
  •  0
  •   Stack Overflow is garbage    15 年前

    这可能只是对编译器的一个提示,即应该将字段读入寄存器,而不是在内存中重复访问它。没有什么理由第一个版本应该是 更糟的 比你列出的第二个更明智。它与编译器生成的代码几乎相同。将对象字段读入寄存器,测试它是否为空,根据需要修改它,然后将其写回内存中的对象字段。

        4
  •  0
  •   martinr    15 年前

    ms.net jit可以 register allocation . 对于这样一个简单的情况,这个临时变量应该被保存在寄存器中,而不是堆栈中。然后,生成的x86字节代码的运行速度应该比在非空情况下读取类成员两次(一次检查空值,一次返回)和在空情况下读取更快。

    代码生成器通常必须将更改写入 物体 一旦它们发生,并在每次引用对象字段时从中读取,否则在某些情况下,线程可能 从未 请参见另一线程对对象所做的更改,反之亦然。

    但是如果 地方的 使用变量编译器假定它不必对局部变量进行更改(甚至不必将其存储在堆栈中),因为访问另一个线程的局部变量不是C允许的。