user1350555
user1350555

Reputation:

How to manage a changing List while performing enumeration?

I have a list of objects (musical notes) that is enumerated on a separate thread as they are played. I am doing this so that I can keep the UI thread responsive.

whilst a note is playing (as part of an enumeration) how can I allow for the fact that a new note may of been added to the List (without the obvious collection modified exception).

I know I could copy the list to a temporary list and enumerate that, but I actually want the list to grow as a user selects more (and this will happen whilst the first note is playing etc).

psuedo logic as is:

onClick()
{
 Queue.Add(theClickedNote)
 Queue.Play() <-- on another thread
}

Play()
{
 if(Playing==true){return ;}

 foreach(note theNote in Queue)
 {
  Note.Play();
  Queue.Remove(theNote);
 }
}

As you can see in the above, each Click event adds a note to the Queue and then invokes a play method on the queue.

the queue enumerates the notes and plays each one in turn before removing the note

I hope I have explained what I am trying to do clearly?

Upvotes: 4

Views: 271

Answers (3)

Mike Zboray
Mike Zboray

Reputation: 40838

Something like this can be used with ConcurrentQueue<T> in .Net 4.

ConcurrentQueue<Note> Queue = new ConcurrentQueue<Note>();

void onClick()
{
  Queue.Enqueue(theClickedNote);

  // start Play on another thread if necessary
}

void Play()
{
  if (Playing) return;

  Note note;
  while(Queue.TryDequeue(out note))
  {
     note.Play();
  }
}

ConcurrentQueue is thread-safe, so no locking needs to be implemented.

Upvotes: 4

YavgenyP
YavgenyP

Reputation: 2123

As suggested by mike z, use the ConcurrentQueue added in .NET 4.0
Along with the other concurrent collections, this queue allows you to add / remove items asynchronosly + work with a snapshot of the underlying collection, by using the GetEnumerator method and iterating with it. Notice you still might need to deal with different situations, such as queue being empty, this can be solved via the BlockingCollection, which take method will block the thread as long as the collection is empty

Upvotes: 0

Alex Gelman
Alex Gelman

Reputation: 534

Instead of using a List you should use a real Queue Then your code will look like this:

    Queue<Note> queue = new Queue<Note>();
    void onClick()
    {
        queue.Enqueue(note);
    }

    void Play()
    {
        if (Playing == true) { return; }

        while (queue.Peek() != null)
        {
            var note = queue.Dequeue();
            note.play();
        }
    }

This code isn't thread safe so you should add locks on the queue but this is the general idea.

Upvotes: 0

Related Questions