.NET Framework 3.5
我有两个使用相同泛型集合的线程。一个线程使用foreach
语句遍历集合:
while(HaveToContinue)
{
// Do work 1
try
{
foreach(var item in myDictionary)
{
// Do something with/to item
}
// Do work 2 (I need to complete the foreach first)
}
catch(InvalidOperationException)
{
}
}
同时另一个线程修改集合:
// The following line causes the InvalidOperationException (in the foreach)
myDictionary.Remove(...);
那么,有没有办法避免这种InvalidOperationException
?如果我可以避免这种异常,我可以一直完成我的工作(工作1 +工作2),相反,每次我发现异常时,我都无法完成工作。
我想使用ManualResetEvent
对象,如下所示:
while(HaveToContinue)
{
// Do work 1
try
{
myResetEvent.Reset();
foreach(var item in myDictionary)
{
// Do something with/to item
}
myResetEvent.Set();
// Do work 2 (I need to complete the foreach first)
}
catch(InvalidOperationException)
{
}
}
每次其他线程修改集合时:
// Expect the foreach is completed
myResetEvent.WaitOne();
// And then modify the collection
myDictionary.Remove(...);
但可能有更好的解决方案。
答案 0 :(得分:1)
如果您使用的是.NET 4,则应使用ConcurrentBag
或ConcurrentDictionary
类,这是线程安全的。
如果您使用的是早期版本,那么最简单(尽管效率低下)的解决方案就是使用lock
。您可以将其实例化为普通对象:
private readonly object sync = new object();
然后,当您需要访问列表时,首先获取锁定:
while (HaveToContinue)
{
// Do work 1
lock (sync)
{
foreach (var item in myDictionary)
{
// Do something with/to item
}
}
// Do work 2 (I need to complete the foreach first)
}
同样在修改集合时,获取相同的锁:
lock (sync)
{
myDictionary.Remove(...);
}
如果您必须对每个项目执行的工作量很大,那么首先获取字典的本地副本,释放锁定,然后继续迭代所述副本,允许赛车线程修改全局词典:
while (HaveToContinue)
{
// Do work 1
Dictionary<Key,Value> localDictionary;
lock (sync)
{
localDictionary = new Dictionary<Key,Value>(myDictionary);
}
foreach (var item in localDictionary)
{
// Do something with/to item
}
// Do work 2 (I need to complete the foreach first)
}