{
private static volatile Singleton instance;
private static object syncRoot = new Object();
private Singleton() {}
public static Singleton Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
instance = new Singleton();
}
}
return instance;
}
}
}
volatile? volatile ? 狂汗, 用了C#这么久,还是第一次看到,顿感惭愧无比。打开MSDN,查了一下解释:
The volatile keyword indicates that a field might be modified by multiple threads that are executing at the same time. Fields that are declared volatile are not subject to compiler optimizations that assume access by a single thread. This ensures that the most up-to-date value is present in the field at all times.
The volatile modifier is usually used for a field that is accessed by multiple threads without using the
这样的解释未免太过简单,而且有些不负责任,无奈放狗google,找到一些解释实在值得商榷,普遍认同的说发是:编译器优化会将当前的field放入CPU cache中,这对单线程程序会有很大好处,但碰到多线程的情况下,因为当前的线程只会更改自己的Cache,由于不能及时更新到内存,会导致其它线程脏读,加上“volatile”关键字则告诉编译器不要做这样的Cache优化,这样就不会产生问题。
其实这种说法是非常错误的,CPU Cache是CPU体系结构自己维护的,与内存之间也是的数据同步也是自动的,是不受编译器控制的(至少X86,X64处理器是这样),Cache什么完全是由CPU缓存预测算法自己决定,何来在ISA层次控制一说?(或许其他架构的处理器可以在ISA层控制,不过这样的处理器不用也罢),就我所知,无论是AMD X2这样的独立L2 Cache处理器,还是酷睿这样的共享L2 Cache处理器,都有一整套缓存策略用以维护缓存和内存数据的一致性,符合Cache预取条件的数据,CPU会自动将它维护在Cache中,更改了这个Cache,必会更改内存,如果是独立缓存体系,还会更改另外的Cache,这是个原子操作,不用也不能干预(除非你有本事拿激光改CPU电路)。
看下面这段代码:
volatile bool IsStop;
//bool IsStop;
int Count;
Thread th;
public Form1()
{
InitializeComponent();
th = new Thread(new ThreadStart(A));
}
private void button1_Click(object sender, EventArgs e)
{
th.Start();
}
void A()
{
while (!IsStop)
{
Thread.Sleep(500);
Count++;
this.Invoke(new Func<int>(() =>
{
label1.Text = Count.ToString();
return 0;
}));
}
}
private void button2_Click(object sender, EventArgs e)
{
new Thread(() => IsStop = true).Start();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
try
{
th.Abort();
}
catch
{ }
}
无论是注释掉volatile bool IsStop;还是bool IsStop;其行为完全一致,点击button2就会使IsStop = true,th那个线程就会停掉,而且编译出的汇编代码完全一样(IL代码不一样),这让我很苦恼,“volatile”完全是个矛盾的,无须存在的东西。就像下面的代码,有没有关键词“volatile”没有任何区别(虽然编译出的IL代码不一样):
int Int2;
volatile int Int3;
int Int4;
void b()
{
Int1++;
00000000 mov qword ptr [rsp+8],rcx
00000005 sub rsp,28h
00000009 nop
0000000a mov rax,642801A2E18h
00000014 mov eax,dword ptr [rax]
00000016 test eax,eax
00000018 je 000000000000001F
0000001a call FFFFFFFFFF7AA7B0
0000001f mov rcx,qword ptr [rsp+30h]
00000024 mov ecx,dword ptr [rcx+000001E4h]
0000002a add ecx,1
0000002d mov rax,qword ptr [rsp+30h]
00000032 mov dword ptr [rax+000001E4h],ecx
Int2++;
00000038 mov rcx,qword ptr [rsp+30h]
0000003d mov ecx,dword ptr [rcx+000001E8h]
00000043 add ecx,1
00000046 mov rax,qword ptr [rsp+30h]
0000004b mov dword ptr [rax+000001E8h],ecx
Int3++;
00000051 mov rcx,qword ptr [rsp+30h]
00000056 mov ecx,dword ptr [rcx+000001ECh]
0000005c add ecx,1
0000005f mov rax,qword ptr [rsp+30h]
00000064 mov dword ptr [rax+000001ECh],ecx
Int4++;
0000006a mov rcx,qword ptr [rsp+30h]
0000006f mov ecx,dword ptr [rcx+000001F0h]
00000075 add ecx,1
00000078 mov rax,qword ptr [rsp+30h]
0000007d mov dword ptr [rax+000001F0h],ecx
}
00000083 jmp 0000000000000085
00000085 add rsp,28h
00000089 rep ret
Int2和Int3的行为无任何区别,那这个“volatile”还有什么意义?
唯一能使我信服的解释就是:“volatile”这个关键词根本就不是为X86、X64这样的处理器准备的,可能是为了安藤这样的处理器吧,因为X86、X64就没有在ISA层提供外部操作Cache的接口,不过话说回来,如果哪种处理器需要手动控制Cache策略,那不如把它扔到垃圾堆里吧,你是在玩处理器还是在写商业代码?殊不知世界上指令性能最强大的处理器还不是酷睿,价格便宜量又足。如果不是需要处理大并发的浮点数据,不选酷睿或者AMD K8,K10实在是很有点吃力不讨好了,况且“volatile”一定程度上破坏了跨平台性,实在有点说不过去.........。
谁有其他观点欢迎讨论,最好有理有据哦,呵呵。