I have been dealing with multithreading issues for a while now. In the past few days I have been trying to ensure all my calls are thread safe. I have just run into an issue that has thrown me. Here is the scenario:
I am attempting to plot a waveform which is passing in ~500 points/sec/waveform and I am displaying 4 waveforms. Upon launch of the application I create 4 objects that have an ObservableCollection property and these properties are bound directly to the xaml in an itemscontrol. All the data comes in and is stored in a Queue and I spawn off a worker thread to pull the data from the queue and update the collection.
Spawn worker thread:QueueProcessThread = Task.Factory.StartNew(() => UpdateWaveFormCollections(WaveForms), tokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
Code to update the collection which runs in a loop (some lines of code omitted for brevity):
waveForm.LastDisplayedTimeStamp = DateTime.Now; // move the last displayed time up
int collectionSize = waveForm.WaveData.Count; while (waveForm.WaveDataBuffer.Count > 0 && waveForm.WaveDataBuffer.Peek().TimeStamp < waveForm.LastDisplayedTimeStamp) { if (waveForm.CurrentPosition >= collectionSize) { waveForm.CurrentPosition = 0; } waveForm.WaveData[waveForm.CurrentPosition] = waveForm.WaveDataBuffer.Dequeue(); waveForm.CurrentPosition++; }
As you can see, I do not actually add/remove items to the collection but instead just update the item at a specific position. This is because I wanted the look like a patient monitor at a hospital.
The problem I am running into is that I realized that I am updating this collection on a non UI thread and that collection is bound to the LineSeries directly. And this works. However, another graph using a StepLineSeries throws an exception when I update that collection on a non UI thread which is expected. How is it possible that I can update the bound collection on a non UI thread? I am concerned by this because 1) occasionally I do get an error that a collection cannot be updated on a non UI thread and 2) when I switched this call to update on the UI thread via a dispatcher the performance was so bad the GUI was unusable. I need to understand why this works so I know how to proceed. I do not want to deploy an application that might fail at any time due to thread mismanagement on my part. I am looking for possible reasons why I can do this but if needed I will try to create a sample. I am just on a very tight time limit to get this released.
Hi Mike,
I've asked our engineering team for comment on this. I'll let you know when they get back to me.
Regarding performance using a dispatcher, I'd expect this if you were running a dispatch per item. That would end up being alot of cross thread activity which can slow things down. A better approach might be to batch update your points. Let's say you'd update n number of points and store the final results in a temporary location not tied to the UI, then after n points have been updated, you push all the results for the batch to the UI thread using a dispatcher and updating the associated points. This would result in fewer dispatcher calls which should improve the performance.
If I batch the updates would I just replace the whole collection or are you suggesting I update, lets say, positions 300 - 350 of the collection in a batch? If you mean update those positions in a batch how would I replace all those positions at once? I will try replacing the entire collection after I batch up changes and see if performance is acceptable. Please let me know if you had a different approach in mind.