0

In my Window, I'm building 3 charts. They aren't loading particularly large sets of data, but it takes a fairly long time for the window to load with the data.

To address this, I'm multithreading it; each set of data is being loaded from its own thread:

private void Page_Loaded(object sender, RoutedEventArgs e)
{
    Thread BasicAlertsThread = new Thread(new ThreadStart(BuildAlertsChart));
    Thread ResolvedAlertsThread = new Thread(new ThreadStart(BuildResolvedAlertsChart));
    Thread DetailedAlertsThread = new Thread(new ThreadStart(BuildAlertDetail));

    BasicAlertsThread.Start();
    ResolvedAlertsThread.Start();
    DetailedAlertsThread.Start();
}

Currently I have each thread updating a label's content when they're done to indicate that set of data is loaded:

lblDescription.Dispatcher.Invoke(() => { lblDescription.Content += "Basic Report Chart loaded..."; });

Here's the method that one of the threads is executing as an example. Each thread does pretty much the same thing (with differences relevant to the data that's being queried).

private void BuildResolvedAlertsChart()
{
    List<AlertsViewModel> ChartData = new List<AlertsViewModel>();
    DateTime StartingDate = DateTime.Now.AddMonths(-1);

    using (ApplicationDbContext Context = new ApplicationDbContext())
    {
        var Towers = Context.Towers.ToList();

        foreach (Tower Tower in Towers)
        {
            var AlertCount = Context.Alerts.Where(x => x.TowerId == Tower.Id && x.Date >= StartingDate && x.IsResolved == true).Count();
            if (AlertCount >= 1)
            {
                ChartData.Add(new AlertsViewModel { Alerts = AlertCount, Tower = Tower.Name });
            }
        }
    }

    this.Dispatcher.Invoke(new Action(() =>
    {
        ccResolvedAlerts.DataSource = ChartData;
        XYDiagram2D Diagram = new XYDiagram2D();
        ccResolvedAlerts.Diagram = Diagram;

        BarStackedSeries2D Series = new BarStackedSeries2D();
        Diagram.Series.Add(Series);

        Series.Brush = Brushes.ForestGreen;

        Series.DisplayName = "Resolved Alerts";
        foreach (AlertsViewModel Model in ChartData)
        {
            Series.Points.Add(new SeriesPoint(Model.Tower, Model.Alerts));
        }

        lblDescription.Dispatcher.Invoke(() => { lblDescription.Text += "Resolved Alerts Chart loaded..."; });
    }));
}

This isn't working quite so well though because the UI still remains pretty inactive until its time to update this message.

I know how to update the message while a single thread is working, but how would I go about doing it while all 3 threads are still busy?

I could use a callback the same way as I'd do with a single thread, but I'm worried about confusing the issue of what's loaded and what isn't if I use the callback from 3 different threads.

Additionally, with this approach I've noticed the UI isn't responding to things like dragging or scrolling until all of the data is loaded.

Ortund
  • 8,095
  • 18
  • 71
  • 139
  • It really depends on what the non UI threads do. Do they access the UI thread. Do they fill a collection which is bound (and hence raising events to the UI) etc. – Ivan Stoev Jun 06 '17 at 11:20
  • Basically they're just collecting information and presenting that in the charts. I've updated the question to show an example. – Ortund Jun 06 '17 at 11:24
  • Well, the example code for the worker thread does 2 things - the first block retrieves data from db and runs on the worker thread. However the second part which binds the data to the chart (the whole block inside `Invoke`) is executing on the UI thread, and if it's slow ... Shortly, looks like the most of the code is executing synchronously on the UI thread, which explains the unresponsiveness. – Ivan Stoev Jun 06 '17 at 11:29
  • If your charts are big - there is not much you can do about this, because all 3 threads populate charts on UI thread (via `Dispatcher.Invoke`). You can try something like splitting your work between multple `Dispatcher.Invoke(..., DispatcherPriority.Background)` to allow other operations with higher priority to be processed between your invokes, but I doubt that will help much. – Evk Jun 06 '17 at 11:47
  • By the way - are your charts interactive (user can some how interact with them) or not? – Evk Jun 06 '17 at 13:10
  • @Evk the charts are using [DevExpress Chart Controls](https://documentation.devexpress.com/#WindowsForms/clsDevExpressXtraChartsChartControltopic) so the most interaction is a mouseover that shows some stats about the data point. Beyond that, not interactive at all – Ortund Jun 06 '17 at 13:30
  • Well that's still counts as interactive. There is a way to render non-interactive controls in separate UI thread, but for your charts it won't work I think (unless you are fine with losing interactivity). – Evk Jun 07 '17 at 08:43

0 Answers0