Exactly how do enumerators work - I know that they build a state machine behind the scenes but if I call GetEnumerator twice will I get two different objects?
If I do something like this
public IEnumerator<T> GetEnumerator()
{
yield return 1;
yield return 2;
}
Can I aquire a lock at the start of the method and is this lock held until the enumerator has returned null or until the enumerator is GC'd?
What happens if the caller resets the enumerator etc -
I guess my question is what is the best way to manage locking when dealing with an enumerator
Note: The client cannot be responsible for thread syncronisation - The class internally needs to be
And finally the example above is a simplification of the problem- The yield statements do a bit more then what I have shown :)
Enumerating through a collection is intrinsically not a thread-safe procedure. Even when a collection is synchronized, other threads could still modify the collection, which causes the enumerator to throw an exception.
The C# GetEnumerator() method is used to convert string object into char enumerator. It returns instance of CharEnumerator. So, you can iterate string through loop.
The foreach operation itself is not thread safe. Say if you run a foreach loop to remove item from forward direction, it'll fail with "Collection was modified; enumeration operation may not execute." exception message.
Yes, each call to your GetEnumerator()
method will create a different object (a new state machine).
You can acquire a lock within an iterator block, but be aware that none of the code in your method will be called until the caller calls MoveNext()
for the first time.
In general, I would advise against holding a lock within an iterator block if at all possible. You don't know what the caller is going to do between calls to MoveNext()
. So long as they dispose of the iterator at some point, the lock will be released eventually, but it still means you're at the mercy of the caller.
If you can give us more information about what you're trying to do, that would help. An alternative design which might be easier to get right would be:
public void DoSomething(Action<T> action)
{
lock (...)
{
// Call action on each element in here
}
}
As John already said it is difficult to give you a good answer. The obvious google search leads to: http://www.codeproject.com/KB/cs/safe_enumerable.aspx
The idea behind this is to lock the IEnumerable instance on construction which has major drawbacks.
The next obvious thing is isolation where you create a copy of your structure and iterate over the copy. This is if naively implemented very memory consuming but it can be worth it if your data set is relatively small.
The best thing would be if your data is immutable then you have automatic thread safety but if you are bound to a collection which count does change you have a mutable data structure. If you could redesign your data structure into an immutable one you are done.
Since it is not a good idea to lock the data for a potentially long time you can implement strategies to achieve thread safety when you take advantage of you exact data structure and use case. If you for example change the data rarely and enumerate it frequently you could implement an optimistic enumerable which does read before start of the enumeration a write counter of your data structure and yields the results as usual. If a write happens in between you can throw an exception to signal the user of your enumerator to try again until he succeeds. This does work but delegates responsibility onto the caller of your enumerable that he needs retry the enumeration until it succeeds.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With