C# NET中的线程同步

C# NET中的线程同步,c#,multithreading,locking,C#,Multithreading,Locking,在我的应用程序中,我有一个对象列表。我将每隔几分钟运行一个进程(线程),它将更新此列表中的值。我将有其他进程(其他线程)只读取这些数据,它们可能同时尝试这样做 更新列表时,我不希望任何其他进程能够读取数据。但是,我不希望只读进程在没有更新发生时互相阻塞。最后,如果进程正在读取数据,则更新数据的进程必须等到读取数据的进程完成 要实现这一点,我应该实现什么样的锁定?是您需要的 ReaderWriterLockSlim是一个类,它将处理您请求的场景。 您可以使用两对功能: EnterWriteLoc

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

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

要实现这一点,我应该实现什么样的锁定?

是您需要的

ReaderWriterLockSlim是一个类,它将处理您请求的场景。 您可以使用两对功能:

  • EnterWriteLock
    ExitWriteLock
  • EnterReadLock
    exitradlock
第一个锁将等待,直到所有其他锁(读和写)都关闭,因此它将像
lock()
一样为您提供访问权限

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

因为没有像with
lock()
语句那样的语法甜点,所以请确保您永远不会因为异常或其他原因忘记退出锁。因此,以如下形式使用它:

try { lock.EnterWriteLock(); //ReadLock //Your code here, which can possibly throw an exception. } finally { lock.ExitWriteLock(); //ReadLock }
public class Example()
{
    public IEnumerable<X> GetReadOnlySnapshot()
    {
        lock (padLock)
        {
            return new ReadOnlyCollection<X>( MasterList );
        }
    }

    private object padLock = new object();
}
尝试 { lock.EnterWriteLock();//ReadLock //您的代码可能会引发异常。 } 最后 { lock.ExitWriteLock();//ReadLock }
您不清楚列表的更新是否涉及修改现有对象,或添加/删除新对象-每种情况下的答案都不同

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

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

try { lock.EnterWriteLock(); //ReadLock //Your code here, which can possibly throw an exception. } finally { lock.ExitWriteLock(); //ReadLock }
public class Example()
{
    public IEnumerable<X> GetReadOnlySnapshot()
    {
        lock (padLock)
        {
            return new ReadOnlyCollection<X>( MasterList );
        }
    }

    private object padLock = new object();
}
public类示例()
{
public IEnumerable GetReadOnlySnapshot()
{
锁(挂锁)
{
返回新的只读集合(主列表);
}
}
私有对象挂锁=新对象();
}

使用
ReadOnlyCollection
包装主列表可确保读者可以在固定内容列表中进行迭代,而不会阻止作者所做的修改。

您可以使用
readerwriterlocksim
。它将完全满足您的要求。但是,它可能比只使用普通的旧
锁要慢。原因是RWLS比
lock
慢约2倍,访问
列表的速度太快,不足以克服RWLS的额外开销。这两种方法都可以测试,但在您的情况下,
readerwriterlocksim
可能会更慢。读写器锁在读写器的数量明显超过写器的数量的情况下表现得更好,当受保护的操作较长且延长时

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

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];
  }
}
公共类示例
{
私人只读列表m_官方;
私有易失性列表mu只读;
公共示例()
{
m_Official=新列表();
m_Readonly=m_官方;
}
公共无效更新()
{
锁(m_官方)
{
//在这里修改正式副本。
m_官方。添加(…);
m_官方。删除(…);
//现在克隆正式副本。
var clone=新列表(m_官方);
//最后交换掉只读副本引用。
m_Readonly=克隆;
}
}
公共对象读取(int索引)
{
//在这里访问只读副本是安全的,因为它是不可变的。
//m_Readonly必须标记为volatile才能正常工作。
返回m_Readonly[索引];
}
}

上面的代码不能完全满足您的需求,因为读者永远不会阻塞。这意味着在编剧们更新官方名单的同时,他们仍将继续进行。但是,在很多情况下,这是可以接受的。

当我看到最初的答案时,它只是第一行,如果链接消失,这是没有用的。不幸的是,苏认为答案没有被编辑,所以我无法撤消我的否决票。这个答案现在很好。现在试试。与此同时,我正在详细阐述。顺便说一句,我想指出另一个答案只包含一个链接,为什么我的;-)?此外,期待msdn链接将很快消失,而不粘贴链接到它的原因听起来像你的过度小心。我时不时地看到(并且正在链接自己)博客帖子的链接,如果这能帮助问题的作者,并可能在线3个多月,那为什么不呢?有时候,这是一个更好的解决方案,而不仅仅是粘贴某人关于某个问题的帖子。该类的名称是链接,而您使用的是“this”-抱歉,这只是一个宠物的愤怒。我不认为MSDN会有死链接,但我见过MS站点在改变内容时重定向中断。我想你是对的。我经常使用[this]()的方式不好,好吧,下次我将尝试格式化链接以使其更具可读性。欢迎停止使用“进程”一词来指线程。