.NET中的线程同步

时间:2011-08-05 23:55:06

标签: c# multithreading locking

在我的应用中,我有一个对象列表。我将每隔几分钟运行一个进程(线程)来更新此列表中的值。我将有其他进程(其他线程)只读取这些数据,他们可能会同时尝试这样做。

更新列表时,我不希望任何其他进程能够读取数据。但是,我不希望在没有更新时,只读进程会相互阻塞。最后,如果进程正在读取数据,则更新数据的进程必须等到读取数据的进程完成。

我应该采用什么样的锁定来实现这一目标?

3 个答案:

答案 0 :(得分:5)

This正是您要找的。

ReaderWriterLockSlim是一个可以处理您要求的场景的类。 您可以使用2对功能:

  • EnterWriteLockExitWriteLock
  • EnterReadLockExitReadLock

第一个会等待,直到所有其他锁都关闭,包括读写,所以它会为你提供lock()会做的访问。

第二个是相互兼容的,您可以在任何给定时间拥有多个读锁。

因为没有像lock()语句那样的语法糖,所以请确保你永远不会忘记退出锁定,因为Exception或其他任何东西。所以以这样的形式使用它:

try
{
lock.EnterWriteLock(); //ReadLock

//Your code here, which can possibly throw an exception.

}
finally
{
lock.ExitWriteLock(); //ReadLock
}

答案 1 :(得分:2)

您不清楚列表的更新是否涉及修改现有对象,或添加/删除新对象 - 每种情况下的答案都不同。

要处理列表中现有项的修改,每个对象都应该处理它自己的锁定。

要允许修改列表而其他人正在迭代它,不要让人们直接访问列表 - 强制他们使用列表的只读副本,如下所示:

public class Example()
{
    public IEnumerable<X> GetReadOnlySnapshot()
    {
        lock (padLock)
        {
            return new ReadOnlyCollection<X>( MasterList );
        }
    }

    private object padLock = new object();
}

使用ReadOnlyCollection<X>包装主列表可确保读者可以遍历固定内容列表,而不会阻止作者进行修改。

答案 2 :(得分:2)

您可以使用ReaderWriterLockSlim。它会精确满足您的要求。但是,它可能比仅使用普通的lock慢。原因是因为RWLS比lock慢〜2倍,访问List的速度要快得多,以至于不足以克服RWLS的额外开销。测试两种方式,但可能 ReaderWriterLockSlim在您的情况下会更慢。读者作家锁在场景中做得更好,因为当受保护的操作很长并且被抽出时,读者数量明显超过作者

但是,让我为您提供另一种选择。处理此类问题的一种常见模式是使用两个单独的列表。一个将作为正式副本,可以接受更新,另一个将作为只读副本。更新正式副本后,您必须克隆它并替换只读副本的引用。这是优雅的,因为读者不需要任何阻止。读者不需要任何阻塞类型的同步的原因是因为我们将只读副本视为不可变的。这是如何做到的。

public class Example
{
  private readonly List<object> m_Official;
  private volatile List<object> m_Readonly;

  public Example()
  {
    m_Official = new List<object>();
    m_Readonly = m_Official;
  }

  public void Update()
  {
    lock (m_Official)
    {
      // Modify the official copy here.
      m_Official.Add(...);
      m_Official.Remove(...);

      // Now clone the official copy.
      var clone = new List<object>(m_Official);

      // And finally swap out the read-only copy reference.
      m_Readonly = clone;
    }
  }

  public object Read(int index)
  {
    // It is safe to access the read-only copy here because it is immutable.
    // m_Readonly must be marked as volatile for this to work correctly.
    return m_Readonly[index];
  }
}

上面的代码不能完全满足您的要求,因为读者永远不会阻止......这意味着当作家正在更新官方列表时,它们仍将发生。但是,在很多场景中,这种情况很可以接受。