Jal*_*aid 14 c# memory-barriers
我最近读到了关于内存障碍和重新排序的问题,现在我对它有些困惑.
请考虑以下情形:
private object _object1 = null;
private object _object2 = null;
private bool _usingObject1 = false;
private object MyObject
{
get
{
if (_usingObject1)
{
return _object1;
}
else
{
return _object2;
}
}
set
{
if (_usingObject1)
{
_object1 = value;
}
else
{
_object2 = value;
}
}
}
private void Update()
{
_usingMethod1 = true;
SomeProperty = FooMethod();
//..
_usingMethod1 = false;
}
Run Code Online (Sandbox Code Playgroud)
在Update方法; 是_usingMethod1 = true获取或设置属性之前始终执行的语句?或者由于重新订购问题我们无法保证?
我们应该使用volatile像
private volatile bool _usingMethod1 = false;
Run Code Online (Sandbox Code Playgroud)如果我们使用lock;can,我们保证锁定中的每个语句都将按顺序执行,如:
private void FooMethod()
{
object locker = new object();
lock (locker)
{
x = 1;
y = a;
i++;
}
}
Run Code Online (Sandbox Code Playgroud)Bri*_*eon 30
记忆障碍的主题非常复杂.它甚至不时让专家绊倒.当我们谈论记忆障碍时,我们真的在结合两种不同的想法.
仅创建两个中的一个的内存屏障有时称为半栅栏.创建两者的内存屏障有时称为全栅栏.
该volatile关键字创建半围栏.易失性字段的读取具有获取语义,而写入具有释放语义.这意味着在读取之前或写入之后不能移动指令.
该lock关键字创建两个边界(入口和出口)的全栅栏.这意味着在每个边界之前或之后都不能移动指令.
但是,如果我们只关心一个线程,那么所有这些都没有意义.由该线程感知的排序始终保留.事实上,没有基本保证,任何计划都无法正常运作.真正的问题是其他线程如何感知读写.这是你需要关注的地方.
那么回答你的问题:
从单个线程的角度来看......是的.从另一个线程的角度来看......没有.
这取决于.这可能有用,但我需要更好地理解你想要实现的目标.
从另一个线程的角度来看......没有.读取和写入可以在锁定边界内自由移动.他们只是无法超越这些界限.这就是为什么其他线程也必须创建内存障碍.
挥发性关键字在这里没有完成任何事情。它的保证非常弱,并不意味着内存障碍。您的代码没有显示创建另一个线程,因此很难猜测是否需要锁定。然而,如果两个线程可以同时执行 Update() 并使用同一个对象,这是一个硬性要求。
请注意,您发布的锁定代码不会锁定任何东西。每个线程都有自己的“locker”对象实例。您必须将其设为类的私有字段,由构造函数或初始值设定项创建。因此:
private object locker = new object();
private void Update()
{
lock (locker)
{
_usingMethod1 = true;
SomeProperty = FooMethod();
//..
_usingMethod1 = false;
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,SomeProperty 分配也会有一场竞赛。