多处理器系统中 volatile 关键字的成本是多少?

问题描述 投票:0回答:6

我们遇到了性能问题,一个潜在的罪魁祸首是集中使用易失性单例。具体代码的形式为:

class foo {
  static volatile instance;
  static object l = new object();

  public static foo Instance {
    if (instance == null)
      lock(l) {
        if (instance == null)
          instance = new foo();
      }

    return instance;
  }
}

它在 8 路盒子上运行,我们看到上下文切换速度达到每秒 500,000 次。典型的系统资源很好 - 25% cpu 利用率、25% 内存利用率、低 IO、无分页等。

使用

volatile
字段是否会导致内存屏障或任何类型的 CPU 缓存重新加载?或者它每次都只是在主内存之后,仅针对该字段?

c# multithreading volatile
6个回答
4
投票

lock
确实会产生内存屏障,因此如果您始终访问锁中的实例,则不需要 volatile。

根据本网站

C# 易失性关键字实现获取和释放语义,这意味着读取时的读内存屏障和写入时的写入内存屏障。


3
投票

易失性不会做的一件事是导致上下文切换。 如果您看到每秒 500,000 次上下文切换,则意味着您的线程正在阻塞某些内容,而 volatile 不是罪魁祸首。


1
投票

可悲的是,单身人士几乎在所有事情上都受到了不好的批评:)

这不是我的专业领域,但据我所知,除了编译器/运行时不出于优化目的重新排序读/写(对变量)之外,易失性没有什么特别的。

编辑:我纠正了。 易失性不仅会引入内存屏障,而且发生的情况(顺便说一句,性能)在很大程度上取决于所涉及的特定 CPU。 请参阅http://dotnetframeworkplanet.blogspot.com/2008/11/volatile-field-and-memory-barrier-look.html

这就是为什么你仍然需要锁。

可能/可能尚未回答的问题:

  1. 您的单例实例实际上在做什么? 也许实例代码需要重构...
  2. 正在运行的进程的线程数是多少? 如果您的线程数异常高,那么 8 路盒子对您没有帮助。
  3. 如果高于预期,为什么?
  4. 系统上还运行着什么?
  5. 性能问题是否一致?

0
投票

在您的示例中,易失性不应该成为任何“减速”的主题。 然而,lock() 可能涉及到内核的大量往返,特别是在存在大量锁争用的情况下。

在这种情况下确实没有必要锁定你的单例,你可以 做

class Foo {
  static Foo instance = new Foo();
  public static Foo FooInstance() {
    return instance ;
  }
}

当然,如果在很多不同的线程中使用“instance”,您仍然需要 lock() 任何改变 Foo 的东西,除非 Foo 的所有方法/属性都是只读的。 例如

 class Foo {
      static Foo instance = new Foo();
      object l = new object();
      int doesntChange = 42;
      int canChange = 123;
      public static Foo FooInstance() {
        return instance ;
      }
      public void Update(int newVal) {
         lock(l) { // you'll get a lot of trouble without this lock if several threads accesses the same FOO. Atleast if they later on read that variable 
            canChange = newVal;
         }

      public int GetFixedVal() {
         return doesntChange; //no need for a lock. the doesntChange is effectivly read only
      }
    }

0
投票

对于单例来说确实没有必要使用 易失性,因为您只需设置它一次 - 并锁定设置它的代码。请参阅 Jon Skeet 关于单例的文章 了解更多信息。


0
投票

简短的回答是,是的,它会创建内存屏障(刷新所有内容并进入主内存,而不仅仅是单个变量),但不会,它不会成为上下文切换的原因。

另外,正如其他人提到的,我不认为这里需要 volatility。

© www.soinside.com 2019 - 2024. All rights reserved.