volatile
关键字指示一个字段可以由多个同时执行的线程修改。 声明为 volatile
的字段不受编译器优化(假定由单个线程访问)的限制。 这样可以确保该字段在任何时间呈现的都是最新的值。
volatile
修饰符通常用于由多个线程访问、但不使用 lock 语句对访问进行序列化的字段。
volatile
关键字可应用于以下类型的字段:
- 引用类型。
- 指针类型(在不安全的上下文中)。 请注意,虽然指针本身可以是可变的,但是它指向的对象不能是可变的。换句话说,不能声明“指向可变对象的指针”。
- 类型,如
sbyte、byte、short、ushort、int、uint、char、float
和bool
。 - 具有以下基类型之一的枚举类型:
byte、sbyte、short、ushort、int
或uint
。 - 已知为引用类型的泛型类型参数。
IntPtr
和UIntPtr
。
可变关键字仅可应用于类或结构的字段。 不能将局部变量声明为 volatile
。
示例
下面的示例说明如何将公共字段变量声明为 volatile
。
class VolatileTest
{
public volatile int i;
public void Test(int _i)
{
i = _i;
}
}
示例
下面的示例演示如何创建辅助线程,并用它与主线程并行执行处理。 有关多线程处理的背景信息,请参阅 线程和线程。
using System;
using System.Threading;
public class Worker
{
// This method is called when the thread is started.
public void DoWork()
{
while (!_shouldStop)
{
Console.WriteLine("Worker thread: working...");
}
Console.WriteLine("Worker thread: terminating gracefully.");
}
public void RequestStop()
{
_shouldStop = true;
}
// Keyword volatile is used as a hint to the compiler that this data
// member is accessed by multiple threads.
private volatile bool _shouldStop;
}
public class WorkerThreadExample
{
static void Main()
{
// Create the worker thread object. This does not start the thread.
Worker workerObject = new Worker();
Thread workerThread = new Thread(workerObject.DoWork);
// Start the worker thread.
workerThread.Start();
Console.WriteLine("Main thread: starting worker thread...");
// Loop until the worker thread activates.
while (!workerThread.IsAlive) ;
// Put the main thread to sleep for 1 millisecond to
// allow the worker thread to do some work.
Thread.Sleep(1);
// Request that the worker thread stop itself.
workerObject.RequestStop();
// Use the Thread.Join method to block the current thread
// until the object's thread terminates.
workerThread.Join();
Console.WriteLine("Main thread: worker thread has terminated.");
}
// Sample output:
// Main thread: starting worker thread...
// Worker thread: working...
// Worker thread: working...
// Worker thread: working...
// Worker thread: working...
// Worker thread: working...
// Worker thread: working...
// Worker thread: terminating gracefully.
// Main thread: worker thread has terminated.
}
c#中多线程修饰符volatile
volatile
是C#中用于控制同步的关键字,其意义是针对程序中一些敏感数据,不允许多线程同时访问,保证数据在任何访问时刻,最多有一个线程访问,以保证数据的完整性,volatile
是修饰变量的修饰符。
1、volatile
的使用场景
多个线程同时访问一个变量,CLR为了效率,允许每个线程进行本地缓存,这就导致了变量的不一致性。volatile
就是为了解决这个问题,volatile
修饰的变量,不允许线程进行本地缓存,每个线程的读写都是直接操作在共享内存上,这就保证了变量始终具有一致性。
2、volatile
关键字可应用于以下类型的字段
(1)、引用类型
(2)、整型,如 sbyte、byte、short、ushort、int、uint、char、float
和 bool
。
(3)、具有整数基类型的枚举类型。
(4)、已知为引用类型的泛型类型参数。
(5)、不能将局部变量声明为 volatile
。
恐怕比较一下volatile
和synchronized
的不同是最容易解释清楚的。volatile
是变 量修饰符,而synchronized
则作用于一段代码或方法;看如下三句get
代码:
int i1; int geti1() {return i1;}
volatile int i2; int geti2() {return i2;}
int i3; synchronized int geti3() {return i3;}
geti1()
得到存储在当前线程中i1
的数值。多个线程有多个i1
变量拷贝,而且这些i1
之间可以互不相同。换句话说,另一个线程可能已经改 变了它线程内的i1
值,而这个值可以和当前线程中的i1
值不相同。事实上,Java有个思想叫“主”内存区域,这里存放了变量目前的“准确值”。每个线程 可以有它自己的变量拷贝,而这个变量拷贝值可以和“主”内存区域里存放的不同。因此实际上存在一种可能:“主”内存区域里的i1值是1,线程1里的i1
值 是2,线程2里的i1
值是3——这在线程1和线程2都改变了它们各自的i1
值,而且这个改变还没来得及传递给“主”内存区域或其他线程时就会发生。
而geti2()
得到的是“主”内存区域的i2
数值。用volatile
修饰后的变量不允许有不同于“主”内存区域的变量拷贝。换句话说,一个变量经 volatile
修饰后在所有线程中必须是同步的;任何线程中改变了它的值,所有其他线程立即获取到了相同的值。理所当然的,volatile
修饰的变量 存取时比一般变量消耗的资源要多一点,因为线程有它自己的变量拷贝更为高效。
既然volatile
关键字已经实现了线程间数据同步,又要synchronized
干什么呢?呵呵,它们之间有两点不同。首 先,synchronized
获得并释放监视器——如果两个线程使用了同一个对象锁,监视器能强制保证代码块同时只被一个线程所执行——这是众所周知的事 实。但是,synchronized
也同步内存:事实上,synchronized
在“主”内存区域同步整个线程的内存。因此,执行geti3()
方法做 了如下几步:
- 线程请求获得监视
this
对象的对象锁(假设未被锁,否则线程等待直到锁释放) - 线程内存的数据被消除,从“主”内存区域中读入(Java虚拟机能优化此步。。。[后面的不知道怎么表达,汗])
- 代码块被执行
- 对于变量的任何改变现在可以安全地写到“主”内存区域中(不过
geti3()
方法不会改变变量值) - 线程释放监视
this
对象的对象锁
因此volatile
只是在线程内存和“主”内存间同步某个变量的值,而synchronized
通过锁定和解锁某个监视器同步所有变量的值。显然 synchronized
要比volatile
消耗更多资源。