我有一个多线程C#应用程序。有一个字典可以同时从N个不同的线程访问。 99%的呼叫来自线程A,其余的来自B,C,......
现在,我只是在每次访问时锁定字典。但这看起来很浪费,因为99%的时间我都在调用锁,线程A已经锁定了。
这样做更有效的方法是什么?
线程B,C,......可能需要某种类型的.Begin和.End调用,然后线程A只需在每次调用时检查一位,看看是否有其他线程正在使用字典。有没有人有这样的代码样本以线程安全的方式实现?
答案 0 :(得分:4)
您需要仔细检查您的探查器数据。
除非存在实际争用,否则Monitor和RWLSlim实际上都不会“硬锁”(如下拉到OS原语);在所有其他情况下,它将使用Interlocked,并且其影响相对最小(除了缓存刷新)。
性能方面,RWLockSlim的创建成本相对较高,而且比Monitor要贵一点。它的优点是允许多个读者和一个作家。
如果您看到硬锁出现,那么您有实际的争用,在这种情况下,您所说的99%/ 1%/ 1%/ 1%/ ...比率可能并不反映现实。
正如之前的海报所指出的那样,你应该使用它 - 在大多数情况下你会偶尔写入和大量读取,否则系统的一致性有点难以实施。如果是这种情况,RWlockSlim应该删除不必要的争用。
底线:这一切都取决于你想要做什么 - 就像这本词典的用途和访问方式一样。有时候扩大锁定以防止占用过多的锁定是有意义的,在某些情况下 - 非常罕见 - 你甚至可能想要使用像Interlocked这样的无锁式原语,甚至在尝试点击“真正的”锁定之前。
也许如果你告诉我们更多关于这个场景的信息,我们可以提供更好的帮助。
答案 1 :(得分:2)
如何执行锁定。对于阅读,您不需要以同样的“硬”方式锁定其他线程,就像您还要更新一样。无论哪种方式,您都可以在读取操作中更松散。我建议调查ReaderWriterLockSlim
(除非你已经在使用它):
class DictionaryHolder
{
private IDictionary<int, string> _data = new Dictionary<int, string>();
private ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
public void Update(int key, string value)
{
_lock.EnterWriteLock();
try
{
_data[key] = value;
}
finally
{
_lock.ExitWriteLock();
}
}
public string GetValue(int key)
{
_lock.EnterReadLock();
try
{
if (_data.ContainsKey(key))
{
return _data[key];
}
finally
{
_lock.ExitReadLock();
}
}
}
这将允许多个线程“同时”从字典中读取,同时在更新时阻止来自其他线程的访问。
答案 2 :(得分:2)
除非探查器告诉您在Monitor.Enter和公司中花费了大量时间,否则您不应该担心这一点 - 通常,获取无人控制的锁或您已经拥有的锁是一个非常快速的操作,应该是性能与您建议的位检查相似。锁定操作通常只有在由于争用而必须阻塞时才会很慢。
答案 3 :(得分:1)
当有许多读者和少数作家时,ReaderWriterLockSlim会提供帮助。但是,根据以下链接,ReaderWriterLockSlim的个别操作性能似乎比Monitor更差:A Performance Comparison of ReaderWriterLockSlim with ReaderWriterLock。
您可以尝试的一个选项是使用Interlocked操作和Event对象来同步线程。但是,您应该再次测量这种方法的性能。
答案 4 :(得分:0)
尝试在.NET 4.0中使用System.Collections.Concurrent.ConcurrentDictionary
它的设计是线程安全的