锁定语句的内存障碍

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

  2. 我们应该使用volatile

    private volatile bool _usingMethod1 = false;
    
    Run Code Online (Sandbox Code Playgroud)
  3. 如果我们使用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关键字创建两个边界(入口和出口)的全栅栏.这意味着在每个边界之前或之后都不能移动指令.

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

那么回答你的问题:

  1. 从单个线程的角度来看......是的.从另一个线程的角度来看......没有.

  2. 这取决于.这可能有用,但我需要更好地理解你想要实现的目标.

  3. 从另一个线程的角度来看......没有.读取和写入可以在锁定边界内自由移动.他们只是无法超越这些界限.这就是为什么其他线程也必须创建内存障碍.

  • 我会将Update方法的全部内容包装在一个锁中.除了记忆障碍,它还保证了原子性,这同样重要.此外,这些无锁的习语(通过volatile,Thread.MemoryBarrier等)难以理解. (3认同)

Han*_*ant 5

挥发性关键字在这里没有完成任何事情。它的保证非常弱,并不意味着内存障碍。您的代码没有显示创建另一个线程,因此很难猜测是否需要锁定。然而,如果两个线程可以同时执行 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 分配也会有一场竞赛。