Reputation:
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
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
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
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