2010-05-21 160 views
3

我正在试着弄清楚这段代码是怎么回事。我有两个线程迭代范围,我试图了解第二个线程调用GetEnumerator()时发生了什么。特别是这条线(T current = start;)似乎在第二个线程中产生了一个新的“实例”。C中的状态机#

看到只有一个DateRange类的实例,我试图理解为什么第二个线程不捕获由第一个线程修改的'current'变量。

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<DateTime> { 

     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(); 
     } 
    } 
+0

IEnumerable通常不是线程安全的,如果您希望多个线程在一个线程上工作,通常最好有一个线程枚举并将工作分配给其他线程,这就是并行Linq和并行foreach的工作方式。 – Mant101 2010-05-21 10:32:06

回答

7

他们都使用相同的IEnumerable<T>,但不同的IEnumerator<T>秒。每次使用IEnumerable输入for each in循环时,GetEnumerator被调用,返回一个单独的具有自己状态的IEnumerator。

+0

我觉得我的大脑刚刚爆炸。 – Pierreten 2010-05-21 06:10:41

+0

对,我也是.. – 2010-05-29 12:37:01

3

迭代器块在引擎盖下实现为隐藏类,它实现了一个相当简单的状态机。每次你打电话给GetEnumerator,它就会返回这个“隐藏”类的新实例,这就是为什么你每次都看到它从头开始的原因。