mardi 11 mai 2021

Improving event subscriptions

In my application, I use DataGridViews to display a lot of data. Much of the data is production data from a separate system. I periodically poll that separate system to update the data, and if the values have changed, I update the DataGridViews.

I've set this up via raising and consuming events, but it is very slow and painful.

The class that retrieves the data from the production system (PIHandler) has these methods:

#region Event Handlers
public event EventHandler<PIValueChangedEventArgs> ValueChanged;
public void OnValueChanged(object sender, PIValueChangedEventArgs valueChangedEventDetails)
{
    ValueChanged?.Invoke(this, valueChangedEventDetails);
}
#endregion
...

//Extract from method that updates the values
//Update all points
for (int i = 0; i < CurrentValues.Count; i++)
{
    AFValue value = CurrentValues[i];
    PIPoint currentPoint = value.PIPoint;
    PreviousValues[currentPoint] = value;
    OnValueChanged(this, new PIValueChangedEventArgs(currentPoint, value));
}

How this works is that the PIHandler looks at the values that have changed, and raises an event for each change. The eventArgs include a parameter for the specific object that changed.

On the subscriber side, I have a custom datagridviewtextboxcell, that includes a reference to the PIPoint that is associated with the cell. I subscribe that cell to the PIManager.ValueChanged event, and do this:

private void PIManager_ValueChanged(object sender, PIValueChangedEventArgs e)
{
    if (e.Point == PiPoint)
    {
        //update the value
        this.Value = e.Value;
    }
}

The simple problem I have is that this is slow. I have around 240 points that I monitor, and at least this many subscribers. So, when 240 points each raise a "valueChanged" event, and each of the 240 subscribers have to check and see if they are the correct consumer, this takes far too long - around two minutes. (I've tested using a static variable to see how many times the _ValueChanged event was handled by the subscribers, and it is over 240,000). I'm surprised that even with this amount of checks it is so slow, but it is what it is.

Note that I have also confirmed that it is the looping thought the events that is taking the time - I have suspended all UI updates on the datagridview before updating the values, as I had assumed that that would be the cause.

What I really want to do is when I raise the event, only target the specific consumer, not just generally broadcast the event to all subscribers. I can't find a good design pattern for this.

I was thinking about not using events - instead, have the PI Manager have a dictionary of Points mapping to subscribers, and when a point changes, find its subscribers, and update them directly. But this is quite a significant rewrite.

Any suggestions?

Aucun commentaire:

Enregistrer un commentaire