我们遇到了性能问题,一个潜在的罪魁祸首是集中使用易失性单例。具体代码的格式为
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%内存util,低IO,无分页等。
使用volatile字段是否会导致内存屏障或任何类型的cpu缓存重新加载?或者它只是每次只追踪主存储器,仅用于该字段?
答案 0 :(得分:4)
lock
确实会导致内存障碍,因此如果您总是在锁中访问实例,则不需要使用volatile。
根据this site:
C#volatile关键字实现了获取和释放语义,这意味着读取时读取内存屏障,写入时写入内存屏障。
答案 1 :(得分:3)
volatile不会做的一件事是导致上下文切换。如果您每秒看到500,000个上下文切换,则意味着您的线程阻塞了某些内容,而volatile是而不是的罪魁祸首。
答案 2 :(得分:1)
可悲的是,单身人士对所有事情都采取了不好的说法:)
这不是我的专业领域,但据我所知,除了编译器/运行时没有重新排序读/写(到变量)以进行优化之外,没有什么特别的易失性。
编辑:我的立场得到了纠正。 volatile不仅会引入内存障碍,而且发生的事情(以及顺便提一下,性能)在很大程度上取决于所涉及的特定CPU。 见http://dotnetframeworkplanet.blogspot.com/2008/11/volatile-field-and-memory-barrier-look.html
这就是你仍然需要锁定的原因。
可能已经/可能尚未回答的问题:
答案 3 :(得分:0)
在你的例子中,volatile不应该是任何“减速”的主题。 然而,lock()可能涉及到内核的大量往返,特别是如果存在大量争用锁定的话。
在这种情况下,确实没有必要锁定你的单身人士 做
class Foo {
static Foo instance = new Foo();
public static Foo FooInstance() {
return instance ;
}
}
当然,如果在许多不同的线程中使用'instance',你仍然必须锁定()改变Foo的任何东西,除非Foo的所有方法/属性都是只读的。 e.g。
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
}
}
答案 4 :(得分:0)
实际上不需要为单例使用volatile,因为你只需要设置一次 - 并锁定代码设置它。有关详细信息,请参阅Jon Skeet's article on singletons。
答案 5 :(得分:0)
简短的回答是,是的,它会创建一个内存屏障(刷新所有内容并转到主内存,而不仅仅是单个变量),但不会导致上下文切换。
此外,正如其他人所说,我不认为这里有必要的挥发性。