5

通过锁定助手,我指的是可以通过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
        }
    }
}

现在,考虑以下用法:

var example = new Example();
new Thread(example.ConcurrentMethod).Start();

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

我知道使用Monitor/锁定具有lock内存屏障的好处,但是当使用诸如此类的锁定助手时,我不明白为什么可以保证这样的屏障。在这种情况下,据我了解,必须修改构造函数:

public Example()
{
    _padlock = new SyncLock();
    Thread.MemoryBarrier();
}

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

编辑Hans Passant 建议创建线程意味着内存屏障。那么怎么样:

var example = new Example();
ThreadPool.QueueUserWorkItem(s => example.ConcurrentMethod());

现在不一定要创建线程...

4

1 回答 1

10

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

此外,作为切向利益的问题,Thread.Sleep也会产生记忆障碍。这很有趣,因为有些人天真地用它Thread.Sleep来模拟线程交错。如果此策略用于解决低锁定代码,那么它可以很好地掩盖您试图查找的问题。

于 2011-07-04T19:33:19.553 回答