12

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

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 foo();
  }
}

这是在一个 8 路盒子上运行的,我们看到上下文切换到每秒 500,000 次。典型的系统资源很好 - 25% cpu util、25% memory util、低 IO、无分页等。

使用 volatile 字段会导致内存屏障或任何类型的 cpu 缓存重新加载吗?或者它只是每次都在主内存之后,只针对那个字段?

4

6 回答 6

4

lock确实会引起内存屏障,因此如果您总是在锁中访问实例,则不需要易失性。

根据这个网站

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

于 2009-06-16T17:06:09.093 回答
3

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

于 2009-06-19T17:39:44.220 回答
1

可悲的是,单身人士几乎对所有事情都说不好:)

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

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

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

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

  1. 你的单例实例实际上在做什么?也许实例代码需要重构......
  2. 正在运行的进程的线程数是多少?如果线程数异常高,8 路盒将无济于事。
  3. 如果高于预期,为什么?
  4. 系统上还在运行什么?
  5. 性能问题是否一致?
于 2009-06-16T17:37:24.423 回答
0

在您的示例中, volatile 不应成为任何“减速”的主题。然而,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
      }
    }
于 2009-06-16T17:42:28.507 回答
0

真的没有必要对单例使用 volatile ,因为您只设置了一次 - 并锁定了设置它的代码。有关更多信息,请参阅Jon Skeet 关于单例的文章。

于 2009-06-16T17:56:45.020 回答
0

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

此外,正如其他人所提到的,我不认为 volatile 在这里是必要的。

于 2015-05-29T18:27:28.710 回答