2010-05-21 61 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,它就會返回這個「隱藏」類的新實例,這就是爲什麼你每次都看到它從頭開始的原因。