【问题标题】:Does a lock around a write guarantee fresh read in another thread? (.Net, memory model)写锁定是否保证在另一个线程中重新读取? (.Net,内存模型)
【发布时间】:2011-03-06 05:35:45
【问题描述】:

假设我有一个属性,其 setter 受锁保护,但 getter 周围没有任何锁,例如

private long _myField;
public long MyProperty
{
    get { return _myField; }
    set { lock(whatever) _myField = value; }
}

除了同步写入(而不是读取)之外,锁定,或者更确切地说 Monitor.Exit,应该会导致 volatile write。现在假设我们有两个线程 A 和 B,会发生以下序列:

  1. A 读取MyProperty 的当前值。
  2. B 将新值写入MyProperty
  3. A 再次读取MyProperty 的当前值。

问:A 现在可以保证看到新值了吗?还是我们的锁只是确保 B 及时写入主内存,而不是其他线程读取新值?或者答案甚至取决于我们是在 .Net 2+ 还是“较弱”的 ECMA 实现中运行?

【问题讨论】:

    标签: c# .net concurrency locking memory-model


    【解决方案1】:

    不,由于读取没有显式内存屏障,因此无法“保证”看到新值。

    您可以使用ReaderWriterLockSlim 来确保 a) 写入相互锁定 b) 读取始终获取新值。

    private readonly ReaderWriterLockSlim _myFieldLock = new ReaderWriterLockSlim();
    private long _myField;
    public long MyProperty
    {
        get 
        {
            _myFieldLock.EnterReadLock();
            try
            {
                return _myField;
            }
            finally
            {
                _myFieldLock.ExitReadLock();
            }
        }
        set
        {
            _myFieldLock.EnterWriteLock();
            try
            {
                _myField = value;
            }
            finally
            {
                _myFieldLock.ExitWriteLock();
            }
        }
    }
    

    【讨论】:

    • 注意:操作码value = _myField好像有误,所以我改成了_myField = value
    • RWLS 的意义何在? getter 和 setter 中的 lock 关键字同样适用于两个线程,而开销很多
    • @hans-passant 如果同时有多个线程在读怎么办?
    • @hans-passant stackoverflow.com/questions/407238/… 不知道答案是否正确,您是否体验过lockReaderWriterLockSlim 相比更好的性能?
    • @scrum - 确实是一个错误,已修复。
    【解决方案2】:

    如果您在 getter 中使用了 Interlocked.Read,您应该始终读取新值。有关内存栅栏的更多信息,请参阅Threading in C#

    【讨论】:

      猜你喜欢
      • 2021-11-09
      • 1970-01-01
      • 1970-01-01
      • 2016-02-24
      • 1970-01-01
      • 2021-12-31
      • 2023-03-24
      • 2020-02-25
      • 2021-06-26
      相关资源
      最近更新 更多