我们遇到了性能问题,一个潜在的罪魁祸首是集中使用易失性单例。具体代码的形式为:
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 缓存重新加载?或者它每次都只是在主内存之后,仅针对该字段?
易失性不会做的一件事是导致上下文切换。 如果您看到每秒 500,000 次上下文切换,则意味着您的线程正在阻塞某些内容,而 volatile 不是罪魁祸首。
可悲的是,单身人士几乎在所有事情上都受到了不好的批评:)
这不是我的专业领域,但据我所知,除了编译器/运行时不出于优化目的重新排序读/写(对变量)之外,易失性没有什么特别的。
编辑:我纠正了。 易失性不仅会引入内存屏障,而且发生的情况(顺便说一句,性能)在很大程度上取决于所涉及的特定 CPU。 请参阅http://dotnetframeworkplanet.blogspot.com/2008/11/volatile-field-and-memory-barrier-look.html
这就是为什么你仍然需要锁。
可能/可能尚未回答的问题:
在您的示例中,易失性不应该成为任何“减速”的主题。 然而,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
}
}
对于单例来说确实没有必要使用 易失性,因为您只需设置它一次 - 并锁定设置它的代码。请参阅 Jon Skeet 关于单例的文章 了解更多信息。
简短的回答是,是的,它会创建内存屏障(刷新所有内容并进入主内存,而不仅仅是单个变量),但不会,它不会成为上下文切换的原因。
另外,正如其他人提到的,我不认为这里需要 volatility。