什么时候应该使用锁?仅在修改数据或访问数据时?
public class Test { static Dictionary<string,object> someList = new Dictionary<string,object>(); static object syncLock = new object(); public static object GetValue(string name) { if (someList.ContainsKey(name)) { return someList[name]; } else { lock(syncLock) { object someValue = GetValueFromSomeWhere(name); someList.Add(name,someValue); } } } }
是否应该锁定整个块或者是否可以将其添加到实际修改中?我的理解是,仍然可能存在一些竞争条件,其中一个呼叫可能没有找到并开始添加它,而另一个呼叫可能也会遇到相同的情况 – 但我不确定.锁定仍然令人困惑.我没有遇到上述类似代码的任何问题,但到目前为止我可能很幸运.上面的任何帮助都会被评估,以及关于如何/何时锁定对象的任何好资源.
解决方法
您也必须在读取时锁定,或者您可以获得不可靠的数据,或者甚至是并发修改在物理上更改目标数据结构时的异常.
在上面的例子中,您需要确保多个线程不会同时尝试添加值,因此在检查它是否已存在时至少需要一个读锁定.否则多个线程可能决定添加,找不到该值(因为此检查未锁定),然后所有尝试依次添加(获取锁定后)
如果您有多次读取且只有少量写入,则可以使用ReaderWriterLockSlim.在上面的代码中,一旦您决定需要添加它,您将获得读锁以进行检查并升级到写锁.在大多数情况下,只需要一个读锁(允许您的读取器线程仍然并行运行).
有一个可用的.Net 4锁定原语here的摘要.在你深入研究多线程代码之前,你应该理解这一点.选择正确的锁定机制可以产生巨大的性能差异.
到目前为止,你是幸运的,这是正确的 – 这是并发错误的常见特征.如果没有针对性的负载测试,它们通常难以重现,这意味着正确的设计(当然,详尽的测试)对于避免令人尴尬和混乱的生产错误至关重要.