我知道在java中,如果有多个线程访问未标记为volatile的变量,则可能会出现一些意外行为。
示例:
private boolean bExit;
while(!bExit) {
checkUserPosition();
updateUserPosition();
}
如果您将bExit变量标记为有毛病,那么可以保证其他线程会看到最近的值。
c#的行为方式是否相同?
更新
例如,在C#中执行此操作:
int counter = ...;
for(...)
{
new Thread(delegate()
{
Interlocked.Decrement(ref counter);
}
}
if(counter == 0)
{
// halt program
}
在上文中,在c#中,您是否必须将计数器变量标记为volatile或者这将按预期工作?
答案 0 :(得分:0)
如果你将bExit变量标记为voilatile,那就会保证这一点 其他线程将看到最新的值。 c#的行为方式是否相同?
是的,但都取决于您访问共享变量的方式。例如,如果以Interlocked.Decrement
之类的线程安全方式访问variyable,即使不使用voilatile也不会出现问题。
volatile
(C# Reference from MSDN)
volatile关键字表示字段可能被修改 多个线程同时执行。是的领域 声明的volatile不受编译器优化的限制 假设由单个线程访问。 这确保了最多 字段中始终存在最新值。
答案 1 :(得分:0)
您需要在这里做一点小心,因为您在示例中选择了两种不同的类型。我的一般经验法则是,volatile最好用于布尔值,而其他一切你可能需要其他类型的同步机制。在C#和java中非常常见的是使用volatile布尔值来例如停止一个循环或任务执行,它具有来自多个线程的可见性:
class Processor
{
private volatile Boolean _stopProcessing = false;
public void process()
{
do
{ ... }
while (!_stopProcessing);
}
public void cancel()
{
_stopProcessing = true;
}
}
以上内容适用于C#或java,在此示例中,volatile关键字表示process方法中的读取将来自实际当前值,而不是缓存值。编译器或VM可以选择以其他方式允许缓存该值,因为进程中的循环不会更改_stopProcessing本身。
所以第一个问题答案是肯定的。
虽然你可以在int上使用volatile,但这只有在你只阅读或写一个值时才有帮助。一旦你做了更复杂的事情,例如递增,您需要一些其他类型的同步,例如您使用Interlocked。在您的第二个示例中,您仍然在阅读计数器的值而没有任何同步,因此您有效地依赖于计数器的其他用法进行同步(例如,使用Interlocked)。
在你的第二个例子中,你最好将你的int标记为易失性,这样你再次可以确定你获得的是当前值而不是某些缓存版本。但是单独使用volatile是不够的,因为底部的读取可能与标准的减量(counter--)重叠,而不是正确使用Interlocked。