title: Using locks doesn't prevent Collection was modified; enumeration operation may not execute
body: <p>This question has been asked many times, but this is a special case.  </p>
  
  <pre><code>public class JobStatusMonitor
  {
      private static List&lt;Job&gt; _runningJobs = new List&lt;Job&gt;();
      private static object myLock = new object();
  
      public static void AddJob(GPSJob input)
      {
          lock (myLock)
              _runningJobs.Add(input);
      }
  
      public static void Start(int pollInterval)
      {
          while (true)
          {
              var removeJobs = new List&lt;GPSJob&gt;();
              lock (myLock)
              {
                  foreach (var job in _runningJobs)
                  {
                      if (job.IsComplete())
                      {
                          removeJobs.Add(job);
                      }
                  }
              }
  
              foreach (var job in removeJobs)
              {
                  _runningJobs.Remove(job);
              }
  
              System.Threading.Thread.Sleep(pollInterval);
          }
      }
  }
  </code></pre>
  
  <p>The list _runningJobs is private, so nothing outside of this class can modify it unless it uses the AddJob method.  The AddJob method uses the same lock as the foreach loop so it shouldn't be able to modify the collection while it is iterating.</p>
  
  <p>My understanding of what should be happening is Start(5000) gets called, there is nothing in the list so it skips to Thread.Sleep().  Background processes add jobs to the list. The while loop returns to the foreach loop and applies the lock.  While iterating over the list any other threads that attempt to add to the collection wait until iteration is complete.  As soon as iteration completes each of those threads will add their job, the lock will not cause race conditions even if there are many threads attempting to add jobs. </p>
  
  <p>What actually happens is any job that is added while this thread is sleeping is added successfully.  Jobs that are added while this list is iterating do not wait for iteration to complete despite the lock.</p>
  
  <p>Why doesn't the lock prevent this error?</p>
  
  <p>EDIT: Copy to a new list within the lock eliminates the error.</p>
  
  <pre><code>public class JobStatusMonitor
  {
      private static List&lt;Job&gt; _runningJobs = new List&lt;Job&gt;();
      private static object myLock = new object();
  
      public static void AddJob(GPSJob input)
      {
          lock (myLock)
          {
             _runningJobs.Add(input);
          }
      }
  
      public static void Start(int pollInterval)
      {
          while (true)
          {
  
              lock (myLock)
              {
                  var completeJobs = _runningJobs.Where(job =&gt; job.IsComplete()).ToList();
                  foreach (var job in completeJobs)
                  {
                      _runningJobs.Remove(job);
                      job.TaskCompletionSource.SetResult(null);
                  }       
              }
  
              System.Threading.Thread.Sleep(pollInterval);
          }
      }
  }
  </code></pre>