It’s important to note that an enumerator does not have exclusive,
thread-safe access to its collection. Even when a collection is
synchronized, other threads can still modify the collection. Therefore,
a collection’s contents can change while enumerating through it, which
will cause the enumerator to throw an exception. So there are three key
ways to safely enumerate a collection:
1. Lock the Collection During Enumeration
To prevent changes to a collection while enumerating through it, you can lock the collection using its
SyncRoot property. Note that you can
grow your own SyncRoot
for collections that don’t have a SyncRoot property. The downside to
this approach is that large collections may be locked for long periods
of time, resulting in poor performance or
deadlocks.
ArrayList list = new ArrayList();
lock (list.SyncRoot)
{
foreach (object obj in list)
{
// do something
}
}
2. Export Collection Contents to an Array
The
best way to guarantee thread-safety is to exclusively own the
collection, and the easiest way to do this is to export the collection’s
contents to an array. The downside to this approach is the time and
memory used to create a separate array.
ArrayList list = new ArrayList();
Array array = list.ToArray();
foreach (object obj in array)
{
// do something
}
3. Catch Enumerator Exceptions
The final approach is simply to catch any exceptions and re-start the
enumeration. The downside to this approach is the enumeration could
restart multiple times for oft-changing collections, and there may be
cases where you do not want to repeat the enumeration.
ArrayList list = new ArrayList();
bool finished = false;
while (!finished)
{
try
{
foreach (object obj in array)
{
// do something
}
finished = true;
}
catch (InvalidOperationException)
{
// collection changed
}
}
No comments:
Post a Comment