DispatcherTimer and UI update limits in C # silverlight

Again, I apologize for the question, which may be simple for all of you. I have a limited understanding of what goes on behind the scenes in Silverlight.

I have a charting application (Visiblox) that I use as a moving area, updated every 20 ms, adding and removing a point. In pseudo code:

List<Point> datapoints= new List<Point>(); Series series = new Series(datapoints); void timer_tick(){ datapoints.Add(new Point); datapoints.RemoveAt(0); // no need to refresh chart, it does refresh automatically } 

When working with 6 rows in this charting tool, he began to show a little lethargy. Changing the tick to 10 ms did not matter, the chart was updated at the same speed, so it seems that 20 ms is a speed limit (user interface or chart?).

I tried with CompositionTarget.Rendering and got the same results: below 20 ms there was no difference in speed.

Then I accidentally turned on both and the speed doubled. Therefore, I tested several threads (2, 3, 4), and the speed doubled, tripled and increased four times. This one does not have locks yet, because I don’t even know what process I need to create a lock, but I didn’t get any data corruption or memory leaks.

The question is why a sluggish 20 ms schedule cannot work for 10 ms, but ridiculously fast with multi-threading? Is the user interface upgrade process faster? Is graph calculation doubled? Or is there a limit on how quickly one DispatcherTimer can be executed?

Thanks!


Edit: I have a background of inline coding, so when I think about threads and timings, I immediately think about switching the pin in the hardware and connecting the area to measure the length of the process. I am new to threads in C # and there are no contacts to connect areas. Is there any way to see graphs of streams of graphics graphically?

+8
multithreading c # silverlight dispatchertimer visiblox
source share
2 answers

The key point here is the understanding that Silverlight provides a maximum frame rate of 60 frames per second by default (configured via the MaxFrameRate property). This means that DispatcherTimer ticks will fire no more than 60 times per second. In addition, all the rendering work takes place in the user interface stream, so that DispatcherTimer works at the speed with which the drawing at best happens, as indicated in the previous poster.

The result of what you do by adding three timers is simply to start the “add data” method 3 times per event loop, and not once, so that it will look as if your graphs are going much faster, but actually frame rate is about the same. You can get the same effect with one DispatcherTimer and just add 3 times more data for each Tick. You can verify this by connecting to the CompositionTarget.Rendering event and counting the frame rate there in parallel.

The ObservableCollection point made earlier is good, but there is a little magic in Visiblox to try to mitigate the consequences of this, so if you add data at a very high speed, chart updates will be compiled at the speed of the render cycle, and unnecessary re-renderings will be deleted.

In addition to the fact that you are tied to the implementation of the IDataSeries ObservableCollection, you can freely implement the IDataSeries interface yourself, for example, maintaining it with a simple list. Just keep in mind that obviously, if you do this, the chart will no longer automatically update when data changes. You can force the chart to be updated by calling Chart.Invalidate () or by changing the range of manually specified ranges.

+4
source share

A DispatcherTimer, which fires the Tick event in the UI thread, is what counts as a low-resolution or low-precision timer, because its interval effectively means "tick no earlier than x from the last tick". If the user interface thread is busy with anything (input processing, chart updating, etc.), then this delays timer events. Moreover, having a DispatcherTimer pool that discards the UI stream at very small intervals will also slow down the responsiveness of your application, because when the Tick event increases, the application cannot respond to input.

So, as you noted, in order to process the data often, you have to go into the background thread. But there are reservations. The fact that you are not currently observing corruption or other errors may just be accidental. If the list changes in the background thread while the foreground thread tries to read it, you will end up crashing (if you're lucky) or see corrupted data.

In your example, you have a comment that says: "there is no need to update the chart, it is automatically updated." This makes me wonder how the chart knows that you have changed the datapoints collection? List<T> does not change events when it is changed. If you used an ObservableCollection<T> , I would indicate that every time you delete / add a point, you potentially update the chart, which can slow down.

But if you are actually using List<T> , then there must be something else (perhaps another timer?) That updates the chart. Perhaps the chart control itself has a built-in automatic update mechanism?

In any case, the problem is a bit complicated but not entirely new . There are ways in which you could maintain a collection in a background thread and communicate with it from a user interface thread. But the faster the user interface is updated, the more likely you are to expect the background thread to release the lock.

One way to minimize this is to use LinkedList<T> instead of List<T> . Adding LinkedList to the end is O (1), so removing the item. A List<T> needs to move everything down by one when you delete an item from the beginning. Using LinkedList, you can block it in the background thread (s), and you minimize the time that you hold the lock. In the user interface thread, you will also need to get the same lock and either copy the list to an array or update the schedule until the lock is saved.

Another possible solution would be to buffer the “chunks” of points in the background thread and send a packet of them to the user interface stream using Dispatcher.BeginInvoke, where you could safely update the collection.

+7
source share

Source: https://habr.com/ru/post/650954/


All Articles