My program will be requesting realtime data from a website. This data can change at any time, so I need to request it repeatedly and frequently to monitor changes. The data is to be displayed on a windows form chart as a histogram along with some moving averages. Based on this information, the user needs to be able to interact with the form to set parameters for a part of the program to take action based on the incoming data. How should I be handling this data? My current plan is to have a separate thread collecting the data and writing it to the main form, but I'm unsure how to monitor that data for changes without A) making the interface unresponsive and B) starting another thread. A is unacceptable for obvious reasons, and if I were to do B I feel like I might as well just throw that code into the thread that's collecting the data.
What you should do in this case is have a worker thread do the polling of the website and queue all of the data it finds into a ConcurrentQueue
. Then have your UI thread periodically poll this queue for new data. You do not want to have that worker thread interacting with the UI thread at all. Do not use Control.Invoke
or other marshaling techniques for situations like this.
public class YourForm : Form
{
private CancellationTokenSource cts = new CancellationTokenSource();
private ConcurrentQueue<YourData> queue = new ConcurrentQueue<YourData>();
private void YourForm_Load(object sender, EventArgs args)
{
Task.Factory.StartNew(Worker, TaskCreationOptions.LongRunning);
}
private void UpdateTimer_Tick(object sender, EventArgs args)
{
YourData item;
while (queue.TryDequeue(out item))
{
// Update the chart here.
}
}
private void Worker()
{
CancellationToken cancellation = cts.Token;
while (!cancellation.WaitHandle.WaitOne(YOUR_POLLING_INTERVAL))
{
YourData item = GetData();
queue.Enqueue(item);
}
}
}
The example above is based on WinForms, but the same principals would carry over to WPF as well. The important points in the example are.
System.Windows.Timer.Timer
(or equivalent if using WPF) to pull the data items from the queue using TryDequeue
. Set the tick frequency to something that provides a good balance between refreshing screen quickly, but not so quickly that it dominates the UI thread's processing time.Enqueue
.CancellationTokenSource
to cancel the operation via Cancel
(which I did not include in the example) and to also drive the polling interval by calling WaitOne
on the WaitHandle
provided by the CancellationToken
.While using Control.Invoke
or other marshaling techniques is not necessarily a bad thing it is not the panacea that it is often made out to be either. Here are but a few disadvantages to using this technique.
The advantages of having the UI thread poll for updates are as follows.