. net中的线程同步
本文关键字:同步 线程 net | 更新日期: 2023-09-27 18:04:34
在我的应用程序中,我有一个对象列表。我将有一个进程(线程)每隔几分钟运行一次,它将更新这个列表中的值。我将有其他进程(其他线程)来读取这些数据,并且它们可能会同时尝试这样做。
当列表被更新时,我不希望任何其他进程能够读取数据。但是,我不希望只读进程在没有发生更新时相互阻塞。最后,如果一个进程正在读取数据,更新数据的进程必须等到读取数据的进程完成。
我应该实现什么样的锁来实现这一点?
这就是你要找的。
ReaderWriterLockSlim是一个类,将处理您所要求的场景。您有两对函数可供使用:
-
EnterWriteLock
和ExitWriteLock
-
EnterReadLock
和ExitReadLock
第一个锁将等待,直到所有其他锁都关闭,读和写都关闭,所以它将像lock()
那样为您提供访问权限。
第二个是相互兼容的,您可以在任何给定的时间有多个读锁。
因为没有像lock()
语句那样的语法糖,所以确保您永远不会因为异常或其他任何事情而忘记退出锁。所以像这样使用它:
您没有明确说明对列表的更新是否涉及修改现有对象,还是添加/删除新对象-每种情况的答案都不同。
要处理对列表中现有项的修改,每个对象都应该处理自己的锁定。
为了允许在其他人迭代列表时修改列表,不允许其他人直接访问列表-强制他们使用只读/只读复制列表的,像这样:
public class Example()
{
public IEnumerable<X> GetReadOnlySnapshot()
{
lock (padLock)
{
return new ReadOnlyCollection<X>( MasterList );
}
}
private object padLock = new object();
}
使用ReadOnlyCollection<X>
来包装主列表可以确保读者可以遍历固定内容的列表,而不会阻塞作者所做的修改。
您可以使用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];
}
}
上面的代码不能完全满足你的需求,因为读取器永远不会阻塞。也就是说,在编剧们更新官方名单的时候,这些故事仍会发生。但是,在很多情况下,这是可以接受的。