18

我最近阅读了有关内存障碍和重新排序问题的信息,现在我对此有些困惑。

考虑以下场景:

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;
}
  1. Update方法;该_usingMethod1 = true语句是否总是在获取或设置属性之前执行?还是由于重新订购问题我们无法保证?

  2. 我们应该使用volatilelike

    private volatile bool _usingMethod1 = false;
    
  3. 如果我们使用lock;can we 保证,那么锁中的每个语句都将按如下顺序执行:

    private void FooMethod()
    {
        object locker = new object();
        lock (locker)
        {
            x = 1;
            y = a;
            i++;
        }
    }
    
4

2 回答 2

35

内存屏障的主题相当复杂。它甚至不时地绊倒专家。当我们谈论记忆障碍时,我们实际上是在结合两种不同的想法。

  • Acquire fence:一个内存屏障,不允许其他读写在栅栏之前移动。
  • 释放栅栏:一个内存屏障,不允许其他读写在栅栏之后移动。

仅创建两个中的一个的内存屏障有时称为半栅栏。创建两者的内存屏障有时称为full-fence

volatile关键字创建半栅栏。volatile 字段的读取具有获取语义,而写入具有释放语义。这意味着在读取之前或写入之后不能移动任何指令。

关键字在两个边界(入口和出口)上lock创建完整的围栏。这意味着在每个边界之前或之后都不能移动任何指令。

但是,如果我们只关注一个线程,那么所有这些都没有实际意义。该线程所感知的排序始终被保留。事实上,如果没有这个基本保证,任何程序都无法正常运行。真正的问题是其他线程如何感知读写。这就是你需要关注的地方。

所以回答你的问题:

  1. 从单个线程的角度来看......是的。从另一个线程的角度来看......不。

  2. 这取决于。这可能会奏效,但我需要更好地了解您要达到的目标。

  3. 从另一个线程的角度来看......不。读取和写入可以在锁的边界内自由移动。他们只是不能移动到这些界限之外。这就是为什么其他线程也创建内存屏障很重要的原因。

于 2010-05-16T20:42:02.297 回答
4

volatile 关键字在这里没有任何作用。它的保证很弱,并不意味着内存屏障。您的代码没有显示正在创建另一个线程,因此很难猜测是否需要锁定。然而,如果两个线程可以同时执行 Update() 并使用相同的对象,这是一个硬性要求。

请注意,您发布的锁定代码不会锁定任何内容。每个线程都有自己的“locker”对象实例。您必须将其设为类的私有字段,由构造函数或初始化程序创建。因此:

private object locker = new object();

private void Update()
{
    lock (locker)
    {
        _usingMethod1 = true;
        SomeProperty = FooMethod();
        //..
        _usingMethod1 = false;
    }
}

请注意,在 SomeProperty 分配上也会有一场比赛。

于 2010-05-16T17:44:34.070 回答