Consider a simple Registry
class accessed by multiple threads:
public class Registry
{
protected readonly Dictionary<int, string> _items = new Dictionary<int, string>();
protected readonly object _lock = new object();
public void Register(int id, string val)
{
lock(_lock)
{
_items.Add(id, val);
}
}
public IEnumerable<int> Ids
{
get
{
lock (_lock)
{
return _items.Keys;
}
}
}
}
and typical usage:
var ids1 = _registry.Ids;//execution deferred until line below
var ids2 = ids1.Select(p => p).ToArray();
This class is not thread safe as it's possible to receive System.InvalidOperationException
Collection was modified; enumeration operation may not execute.
when ids2 is assigned if another thread calls Register
as the execution of _items.Keys
is not performed under the lock!
This can be rectified by modifying Ids
to return an IList
:
public IList<int> Ids
{
get
{
lock (_lock)
{
return _items.Keys.ToList();
}
}
}
but then you lose a lot of the 'goodness' of deferred execution, for example
var ids = _registry.Ids.First(); //much slower!
So,
1) In this particular case are there any thread-safe options that involve IEnumerable
2) What are some best practices when working with IEnumerable
and locks ?