State machines in C#
I'm trying to work out what's going on with this code. I have two threads iterating over the range and I'm trying to understand what is happening when the second thread calls GetEnumerator(). This line in particular (T current = start;), seems to spawn a new 'instance' in this method by the second thread.
Seeing that there is only one instance of the DateRange class, I'm trying to understand why the second thread doesn't capture the 'current' variable being modified by the first thread.
class Program {
static void Main(string[] args) {
var daterange = new DateRange(DateTime.Now, DateTime.Now.AddDays(10), new TimeSpan(24, 0, 0));
var ts1 = new ThreadStart(delegate {
foreach (var date in daterange) {
Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + " " + date);
}
});
var ts2 = new ThreadStart(delegate {
foreach (var date in daterange) {
Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + " " + date);
}
});
Thread t1 = new Thread(ts1);
Thread t2 = new Thread(ts2);
t1.Start();
Thread.Sleep(4000);
t2.Start();
Console.Read();
}
}
public class DateRange : Range<Date开发者_JS百科Time> {
public DateTime Start { get; private set; }
public DateTime End { get; private set; }
public TimeSpan SkipValue { get; private set; }
public DateRange(DateTime start, DateTime end, TimeSpan skip) : base(start, end) {
SkipValue = skip;
}
public override DateTime GetNextElement(DateTime current) {
return current.Add(SkipValue);
}
}
public abstract class Range<T> : IEnumerable<T> where T : IComparable<T> {
readonly T start;
readonly T end;
public Range(T start, T end) {
if (start.CompareTo(end) > 0)
throw new ArgumentException("Start value greater than end value");
this.start = start;
this.end = end;
}
public abstract T GetNextElement(T currentElement);
public IEnumerator<T> GetEnumerator() {
T current = start;
do {
Thread.Sleep(1000);
yield return current;
current = GetNextElement(current);
} while (current.CompareTo(end) < 1);
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
return GetEnumerator();
}
}
They both use the same IEnumerable<T>
, but different IEnumerator<T>
s. Each time you enter a for each in loop with a IEnumerable, GetEnumerator
is called, returning a separate IEnumerator with its own state.
Iterator blocks are implemented, under the hood, as a hidden class that implements a fairly simple state machine. Each time you call GetEnumerator
, then, it's returning a new instance of this "hidden" class and that's why you see it start from the beginning each time.
精彩评论