逖靖寒的世界

每天进步一点点

今天突然理解了“volatile”

首先咱们可以看一下MSDN对volatile的定义:
The volatile keyword indicates that a field can be modified in the program by something such as the operating system, the hardware, or a concurrently executing thread.

The system always reads the current value of a volatile object at the point it is requested, even if the previous instruction asked for a value from the same object. Also, the value of the object is written immediately on assignment.

The volatile modifier is usually used for a field that is accessed by multiple threads without using the lock statement to serialize access. Using the volatile modifier ensures that one thread retrieves the most up-to-date value written by another thread.

然后呢,咱们可以看到在Singleton模式中对多线程的优化

 1using System;
 2using System.Configuration;
 3
 4namespace HeadFirstDesignPatterns.Singleton.InterestRate
 5{
 6    /// <summary>
 7    /// Summary description for RateSingleton.
 8    /// </summary>

 9    public class RateSingleton
10    {
11        private volatile static RateSingleton uniqueInstance;
12        private static object syncRoot = new Object();
13
14        private double currentRate = 
15            Convert.ToDouble(ConfigurationSettings.AppSettings["CurrentInterestRate"]);
16
17        public double CurrentRate
18        {
19            get
20            {
21                return currentRate;
22            }

23            set
24            {
25                currentRate = value;
26            }

27        }

28
29        private RateSingleton()
30        {}
31
32        public static RateSingleton GetInstance()
33        {
34            //The approach below ensures that only one instance is created and only 
35            //when the instance is needed. Also, the uniqueInstance variable is 
36            //declared to be volatile to ensure that assignment to the instance variable 
37            //completes before the instance variable can be accessed. Lastly, 
38            //this approach uses a syncRoot instance to lock on, rather than 
39            //locking on the type itself, to avoid deadlocks.
40
41            if(uniqueInstance == null)
42            {
43                lock (syncRoot)
44                {
45                    if(uniqueInstance == null)
46                    {
47                        uniqueInstance = new RateSingleton();
48                    }

49                }

50            }

51            return uniqueInstance;
52        }

53    }

54}

55

posted on 2007-11-12 12:36 逖靖寒 阅读(4486) 评论(7) 编辑 收藏

Feedback

#1楼 2007-12-05 19:21 dlwxq[未注册用户]

其实Volatile是由于编译器优化所造成的一个Bug而引入的关键字。
int a = 10;
int b = a;
int c = a;
理论上来讲每次使用a的时候都应该从a的地址来读取变量值,但是这存在一个效率问题,就是每次使用a都要去内存中取变量值,然后再通过系统总线传到CPU处理,这样开销会很大。所以那些编译器优化者故作聪明,把a读进CPU的cache里,像上面的代码,假如a在赋值期间没有被改变,就直接从CPU的cache里取a的副本来进行赋值。但是bug也显而易见,当a在赋给b之后,可能a已经被另一个线程改变而重新写回了内存,但这个线程并不知道,依旧按照原来的计划从CPU的cache里读a的副本进来赋值给c,结果不幸发生了。
于是编译器的开发者为了补救这一bug,提供了一个Volatile让开发人员为他们的过失埋单,或者说提供给开发人员了一个选择效率的权利。当变量加上了Volatile时,编译器就老老实实的每次都从内存中读取这个变量值,否则就还按照优化的方案从cache里读。
 回复 引用   

#2楼 2007-12-05 20:33 郭鹏[未注册用户]

@dlwxq
呵呵 王老师说得对
 回复 引用   

#3楼 2008-04-17 23:20 AlickG[未注册用户]

1楼的说法是错误的,既然volatile是编译阶段的控制,那如何能够控制CPU是从内存还是cache中取数据。

volatile是告诉编译器不能从寄存器中取数据,而是从内存中取,至于CPU到底是从内存中取还是在data cache/l2 cache中取,取决于volatile指的数据对应的memory对于CPU是否可cache的,只要是可cache的并且能够cache命中,CPU还是直接从cache中取。
 回复 引用   

#4楼 2008-05-12 16:34 Bob.Liu[未注册用户]

MSDN Library中写到:

volatile 关键字表示字段可能被多个并发执行线程修改。声明为 volatile 的字段不受编译器优化(假定由单个线程访问)的限制。这样可以确保该字段在任何时间呈现的都是最新的值。[从这里可以看出,如果使用了该修饰符,则应该是每次都从内存中读取的,对吧!]

volatile 修饰符通常用于由多个线程访问而不使用 lock 语句(C# 参考)语句对访问进行序列化的字段。有关在多线程方案中使用 volatile 的示例,请参见如何:创建和终止线程(C# 编程指南)。

volatile 关键字可应用于以下类型:

引用类型。

指针类型(在不安全的上下文中)。

整型,如 sbyte、byte、short、ushort、int、uint、char、float 和 bool。

具有整数基类型的枚举类型。

已知为引用类型的泛型类型参数。

IntPtr 和 UIntPtr。

所涉及的类型必须是类或结构的字段。不能将局部变量声明为 volatile。
 回复 引用   

#5楼 2008-12-17 23:42 tom.zheng[未注册用户]

3楼说的非常准确, 大家不要拿什么 msdn引用的一堆废话来忽悠, 理解了就没啥了。volatile 对于cache过的memory而又有多master的,这个关键字的作用难说了  回复 引用   

#6楼 2009-07-03 09:55 踏风而过[未注册用户]

事实上,3楼说的并不精确,自然CPU可能是从cache1/2中取数据,这一点都不假,可是如果其他线程修改了内存的数据后,相应的cache中的页会被置为无效,那么下次再来读取肯定不会cache命中,这样被volatile修饰的值始终还是真正被修改过的值,这些在操作系统的分页机制中有详细的描述。所以我认为5楼的理解还是有偏差的。不知道我说的对否,欢迎讨论!  回复 引用   

#7楼 2009-07-10 11:24 dreamzqw[未注册用户]

有时虽然cache中的页被置为无效,但有时可能会出现Memery的值在CPU的寄存中,  回复 引用