线程安全使用锁定助手(关于内存障碍)

Oha*_*der 5 c# multithreading locking memory-barriers

通过锁定助手,我指的是可以通过using语句实现锁定的一次性对象.例如,考虑Jon Skeet的MiscUtilSyncLock类的典型用法:

public class Example
{
    private readonly SyncLock _padlock;

    public Example()
    {
        _padlock = new SyncLock();
    }

    public void ConcurrentMethod()
    {
        using (_padlock.Lock())
        {
            // Now own the padlock - do concurrent stuff
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,考虑以下用法:

var example = new Example();
new Thread(example.ConcurrentMethod).Start();
Run Code Online (Sandbox Code Playgroud)

我的问题是这个 - 因为example是在一个线程上创建并ConcurrentMethod在另一个线程上调用,所以ConcurrentMethod线程不会忘记_padock在构造函数中的赋值(由于线程缓存/读写重新排序),因而抛出一个NullReferenceException(在_padLock本身)?

我知道锁定Monitor/ lock具有内存障碍的好处,但是当使用这样的锁定助手时,我无法理解为什么会有这样的障碍得到保证.在这种情况下,据我所知,构造函数必须修改:

public Example()
{
    _padlock = new SyncLock();
    Thread.MemoryBarrier();
}
Run Code Online (Sandbox Code Playgroud)

来源:了解低锁技术在多线程应用中的影响

编辑 Hans Passant认为创建线程意味着内存障碍.那怎么样:

var example = new Example();
ThreadPool.QueueUserWorkItem(s => example.ConcurrentMethod());
Run Code Online (Sandbox Code Playgroud)

现在一个线程不一定被创建......

Bri*_*eon 10

不,您不需要做任何特别的事情来保证创建内存障碍.这是因为几乎所有用于获取在另一个线程上执行的方法的机制都会在调用线程上生成一个释放栅栏屏障,并在工作线程上生成一个aquire-fence屏障(实际上它们可能是完整的栅栏障碍).因此,无论是QueueUserWorkItemThread.Start将自动插入必要的障碍.你的代码是安全的.

此外,作为切向感兴趣的问题Thread.Sleep也会产生记忆障碍.这很有趣,因为有些人天真地Thread.Sleep用来模拟线程交错.如果使用此策略对低锁定代码进行故障排除,则可以很好地掩盖您尝试查找的问题.

  • 'Thread.Sleep的+1也会产生一个内存障碍' - 非常有趣的概念,如果确实如此 (4认同)