我现在已经看过几次,但我不确定它是不是真的不对。
考虑以下示例类:
class Foo
{
List<string> lockedList = new List<string>();
public void ReplaceList(IEnumerable<string> items)
{
var newList = new List<string>(items);
lock (lockedList)
{
lockedList = newList;
}
}
public void Add(string newItem)
{
lock (lockedList)
{
lockedList.Add(newItem);
}
}
public void Contains(string item)
{
lock (lockedList)
{
lockedList.Contains(item);
}
}
}
ReplaceList会在锁定时覆盖lockedList
。一旦所有后续调用者实际上将锁定新值。在ReplaceList退出锁定之前,他们可以进入锁定状态。
尽管通过替换锁定对象来提升标志,但这段代码实际上可能正常工作。只要赋值是锁的最后一个语句,就不再需要运行同步代码。
除了确保分配保持在锁定块结束时增加的维护成本之外,还有另一个原因可以避免这种情况吗?
答案 0 :(得分:3)
因此,首先,由于您访问该字段的具体情况,您提供的具体解决方案并不安全。
获得可行的解决方案非常简单。只是不要创建一个新列表;相反,清除它并添加新项目:
class Foo
{
private List<string> lockedList = new List<string>();
public void ReplaceList(IEnumerable<string> items)
{
lock (lockedList)
{
lockedList.Clear();
lockedList.AddRange(items);
}
}
public void Add(string newItem)
{
lock (lockedList)
{
lockedList.Add(newItem);
}
}
public void Contains(string item)
{
lock (lockedList)
{
lockedList.Contains(item);
}
}
}
现在你的领域实际上并没有改变,你也不必担心可能导致的所有问题。
至于问题中的代码如何破解,只需调用Add
或Contains
来读取字段,获取列表,锁定列表,然后再使用另一个线程替换该字段。当您第二次读取该字段时,在已经获得锁定值之后,该值可能已更改,因此您最终会变异或从列表中读取另一个呼叫者不会受到限制从访问。
所有这一切,虽然改变lockedList
变量是一个真的坏主意,你应该毫无疑问地避免改变它,如上所示,你也可以确保你只是实际阅读该字段一次,而不是重复读取,并且您仍然确保每个列表只能在任何时间从单个线程访问:
class Foo
{
private volatile List<string> lockedList = new List<string>();
public void ReplaceList(IEnumerable<string> items)
{
lockedList = new List<string>(items);
}
public void Add(string newItem)
{
var localList = lockedList;
lock (localList)
{
localList.Add(newItem);
}
}
public void Contains(string item)
{
var localList = lockedList;
lock (localList)
{
localList.Contains(item);
}
}
}
请注意,此修复的问题并不是要改变要锁定的对象的字段(这不是本身问题,尽管是非常不好的做法),而是从lock
语句内部不断地从字段的所有用法中获取新值并期望该值永远不会改变,只有它可以。
这将更难维护,非常脆弱,并且更难以理解或确保正确性,所以再次做这样的事情。
答案 1 :(得分:2)
我意识到在锁定它之前读取锁定对象的字段已经完成了;所以这永远不会是正确的。如果另一个线程在更改值之前尝试进入锁定,它将最终进入其锁定块并锁定旧值,但该字段将具有新值。然后它将使用新值而不锁定它。
示例:
ReplaceList
并输入锁定。Add
。它到达锁定并被阻止。lockedList
。 Contains
,它获取lockedList
的新值并取出锁定。lockedList
进入锁定,并将项目添加到新列表中,而不会锁定新列表。