我正在尝试锁定一个通过其元素进行迭代的对象。我的arraylist allThreads真的没有被锁定,因为在执行“foreach”期间,我得到一个例外,说“收集被修改;枚举操作可能无法执行”。我以为那是锁定的全部交易?
lock (mApp.allThreads)
{
foreach (Thread t in mApp.allThreads)
if (t.Name == "OpcDAWriter" && t != Thread.CurrentThread)
t.Join();
}
答案 0 :(得分:3)
我想你可能误解了lock
对你的影响。它不阻止其他代码操纵您锁定的对象。它的作用是防止一个线程在一个对象上获取long,而另一个线程持有该锁。
如果你想阻止一个线程在另一个线程迭代它时操纵集合,你需要将迭代代码和操作代码放在lock
块中,锁定在同一个对象上。
简单样本:
class LockDemo
{
private IList<string> _items;
private object _lock = new object();
public LockDemo()
{
_items = new List<string>(new[] { "one", "two", "three" });
}
public void RemoveItem(string item)
{
lock (_lock)
{
_items.Remove(item);
}
}
public void DoSomethingThatIteratesOverTheList()
{
lock (_lock)
{
foreach (var item in _items)
{
// do something with item
}
}
}
}
注意所有对列表的访问(本例中的构造函数除外)都包含在lock
块中,这些块都锁定在同一个对象上。另请注意,此对象不是列表本身,而是仅用于锁定目的的对象。这表明lock
不会像这样锁定对象,但提供了一种机制来控制代码的哪些部分可能由不同的线程并行执行。
答案 1 :(得分:0)
您正在加入t
线程,因此它可能会从mApp.allThreads
中删除,或者由于连接而发生其他事情,从而修改了集合。
另外,仅仅因为您正在查看对象,并非所有其他方法都可以锁定它,只有在访问对象的所有方法锁定它时,锁才有效。您可以尝试使用外部对象作为锁定参数,即:
private readonly _lock = new object();
[...]
lock(_lock)
{
foreach....
}
但我怀疑这会改变什么。