This is a migrated thread and some comments may be shown as answers.
ConcurrentModificationException when setting ScatterLineSeries.ItemsSource
9 Answers 16 Views
This is a migrated thread and some comments may be shown as answers.
Bill
Top achievements
Rank 1
Bill asked on 12 Dec 2018, 03:05 PM

I am trying to set the ItemSource for a chart in a Task method, like this:

Task ShowData() 
{
  return Task.Factory.StartNew(() => {
    // do calculations here...
    LineSeries1.ItemsSource = _chartLines;
  }
}

However, when run, I get the following exception: Unhandled Exception: Java.Util.ConcurrentModificationException

When I change this to a normal void method it works fine. Any pointers as to how to fix this?

My Telerik.Xamarin.Android.Chart dll is v4.0.30319.

9 Answers, 1 is accepted

Sort by
0
Bill
Top achievements
Rank 1
answered on 12 Dec 2018, 03:10 PM

This should go at the end of that code snippet I posted:

, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
0
Lance | Manager Technical Support
Telerik team
answered on 12 Dec 2018, 03:23 PM
Hi Jim,

Without the rest of the code I can't give you a definitive answer. What happens when you marshal the task's work back to the UI thread when its done:

Task ShowData()
{
  return Task.Factory.StartNew(() => {
    // do calculations here...
 
    Device.BeginInvokeOnMainThread(()=> { LineSeries1.ItemsSource = _chartLines; };
     
  }
}

If this doesn't help, can you please reply with the rest of the code, I particularly need to see the following:

- The chart definition (e.g. XAML)
- The methods that invoke the task (e.g. is ShowData in OnAppearing?)
- Any other code that will help me build the reprodicible (e.g. model class, view model class, etc)

Regards,
Lance | Tech Support Engineer, Sr.
Progress Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Bill
Top achievements
Rank 1
answered on 12 Dec 2018, 03:37 PM

Here's my chart xaml:

<chart:RadCartesianChart x:Name="ChartResults" VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand" Margin="1,1,1,1" Grid.Row="0" Grid.Column="0">
    <chart:RadCartesianChart.HorizontalAxis>
        <chart:NumericalAxis x:Name="HorizontalAxis" />
    </chart:RadCartesianChart.HorizontalAxis>
    <chart:RadCartesianChart.VerticalAxis>
        <chart:NumericalAxis x:Name="YAxis2" LabelFormat="0.##" />
    </chart:RadCartesianChart.VerticalAxis>
    <chart:RadCartesianChart.Grid>
        <chart:CartesianChartGrid MajorLinesVisibility="XY" />
    </chart:RadCartesianChart.Grid>
    <chart:RadCartesianChart.Series>
        <chart:ScatterLineSeries x:Name="LineSeries1" Stroke="CornflowerBlue" StrokeThickness="1" />
    </chart:RadCartesianChart.Series>
</chart:RadCartesianChart>

 

ShowData is called by a Timer. The Timer is initialised at the end of the OnAppearing method:

ShowWaveformDelegate showWaveform = DoShowWaveform
MyEvent.Callback = showWaveform
Globals.MyTimer = new Timer { Interval = 1000 };
Globals.MyTimer.Elapsed += OnTimedEvent;
Globals.MyTimer.Start();

 

private static void OnTimedEvent(object source, ElapsedEventArgs e
{
  Globals.MyTimer.Stop();
  MyEvent.Run();
}

 

private async void DoShowWaveform(Data data)
{
  await ShowWaveform(data);
}

 

The idea here is to update the chart every second, but when the chart is being updated, stop the timer, then restart the timers after it has been updated.

0
Bill
Top achievements
Rank 1
answered on 12 Dec 2018, 03:41 PM

MyEvent looks like this:

public static Delegate Callback { get; set; }
 
public static async void Run()
{
    try
        {
            // collect data
 
            Callback?.DynamicInvoke(data);
        }
        catch (Exception e)
        {
            // ignored
        }
    }
}
0
Lance | Manager Technical Support
Telerik team
answered on 12 Dec 2018, 08:05 PM
Hi Bill,

Unfortunately, the code is not complete enough for me to recreate the exception as there are missing pieces and infrastructure. Regardless, I suspect the reason for the error is the code is multiple threads are trying to update the series simultaneously.

It's recommended that you use Device.StartTimer in Xamarin.Forms as it will use the proper clock for the device. Here's a good discussion on StackOverflow about System Timer vs Device.StartTimer.


Demo

You could use the PropertyChanged event of the series to know when the last time the ItemsSource was set before starting the 1 second timer again.

// subscribe to Propertychanged
LineSeries1.PropertyChanged += LineSeries1_PropertyChanged;
 
 
// the event handler that will fire off another 1 second timer
private void LineSeries1_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
    // this will be true is the ItemsSource was reset
    if (e.PropertyName == nameof(ScatterLineSeries.ItemsSource))
    {
        // Get the next data in 1 second
        Device.StartTimer(TimeSpan.FromMilliseconds(1000), OnTick);
    }
}

To stop each timer, you return false (returning True continues the timer)

private bool OnTick()
{
    Task.Run(async () =>
    {
        // Get Data From sensors
        await Task.Delay(200);
 
        // Set the ItemsSource
        Device.BeginInvokeOnMainThread(() =>
        {
            LineSeries1.ItemsSource = _lineSeriesData;
        });
    });
 
    // Stop the timer
    return false;
}

With this approach you have a reliable order of operations. Here's the debug output from my attached demo to verify everything is occurringas intended:

[0:] OnTick
[0:] Reading Sensor Data
[0:] Setting ItemsSource
[0:] OnTick
[0:] Reading Sensor Data
[0:] Setting ItemsSource
[0:] OnTick
[0:] Reading Sensor Data
[0:] Setting ItemsSource
[0:] OnTick
[0:] Reading Sensor Data
[0:] Setting ItemsSource


Regards,
Lance | Tech Support Engineer, Sr.
Progress Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Bill
Top achievements
Rank 1
answered on 13 Dec 2018, 12:08 PM

Thanks for the example. It works, but the timer isn't being retriggered after the LineSeries1.ItemSource is set for the second time. Here's my debug output:

[0:] OnTick
[0:] stop timer
[0:] do reading
[0:] show waveform
[0:] start timer
[0:] OnTick
[0:] stop timer
[0:] do reading
[0:] show waveform

My code is the same as your, except I do await ShowWaveform(data) in the OnTick method, and I set LineSeries1 in my ShowWaveform method, like this:

Device.BeginInvokeOnMainThread(() =>
{
  LineSeries1.ItemsSource?.Clear();
  LineSeries1.ItemsSource = _chartLines;
});

I also tried omitting the Clear line, but still no joy.

 

 

 

0
Lance | Manager Technical Support
Telerik team
answered on 13 Dec 2018, 03:50 PM
Hello Bill,

You don't need to call Clear on the ItemsSource because you're resetting the value in the next line. Since ItemsSource is a DependencyProperty, setting it replaces the previous value in the control.

I can confirm that the LineSeries's ItemsSource is working as expected (as demonstrated in my attached demo). I've reattached it here as a runnable solution so you can see this first-hand. When the page launches, the ItemsSource is updated continuously.

I'm unable to directly diagnose the issue you're having, but as you suspect it might be due to using await in the tick instead of my recommended approach (you would do the async call where I have Task.Delay).


Further Assistance using Task

Telerik Forums specifically covers the Telerik controls and their APIs, the problem you're having isn't related to the Chart's APIs. For example, if you swapped out the Chart for a plain Xamarin ListView, you'd have the same problem.

If you'd like assistance in redesigning or debugging the architecture, I recommend posting in StackOverflow or the Xamarin Forums. This isn't an uncommon threading issue and you'll be able to get assistance from the Xamarin and .NET community who've previously built solutions for this scenario.


Professional Services

If you do get completely stuck, and are unable to find any solution, we have partners who are specialized  work on development problems like this and help you build out the app. You can contact them here: Professional Services - Architecture Review or Professional Services - Custom App Dev


Regards,
Lance | Tech Support Engineer, Sr.
Progress Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Bill
Top achievements
Rank 1
answered on 14 Dec 2018, 11:42 AM

I'm not so sure that this is a general async issue. The first time the timer is triggered, the following line:

LineSeries1.ItemsSource = _chartLines;

Correctly causes the LineSeries1_PropertyChanged event to be triggered. The second time the ItemsSource is set, the LineSeries1_PropertyChanged event is not triggered.

0
Lance | Manager Technical Support
Telerik team
answered on 14 Dec 2018, 03:02 PM
Hello Bill,

This is what I'm referring to. The fact that PropertyChanged event is not firing, means that the logic in the task is not actually setting ItemsSource.

If you can isolate this in a separate runnable project, send that project to us in a support ticket (you can create one here). I'll ask the development team to to take a look and offer suggestions on how to restructure.

Regards,
Lance | Tech Support Engineer, Sr.
Progress Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
Tags
Chart
Asked by
Bill
Top achievements
Rank 1
Answers by
Bill
Top achievements
Rank 1
Lance | Manager Technical Support
Telerik team
Share this question
or