Search code examples
c#eventsreal-timebuffering

How to handle processing real-time events that fire before processing of previous event is complete (C#)


Suppose we have a listener to a real-time event that executes some block of code upon the event being triggered.

for our discussion lets say we have a MyTime class, that has a member currentTime.

we have set it up so that whenever the computer clock changes, the currentTime is set to the value of current time. We've implemented the property changed INotifyPropertyChanged interface for our currentTime object:

public event PropertyChangedEventHandler PropertyChanged;


       public string currentTime
            {
                get { return _currentTime; }
                set { _currentTime= value; this.NotifyPropertyChanged("currentTime"); } 
             }


    public void NotifyPropertyChanged(object sender, PropertyChangedEventArgs e) {

    if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(name));

    }

Some other class, say ProcessTime is listening to this event:

TimeChanged += new PropertyChangedEventHandler(PropertyChanged};

and it has a function that will execute something:

public void TimeChanged(object sender, PropertyChangedEventArgs e)
{

// Process lots of calculations

}

Since our computer clock changes all the time, it will keep firing the event. In my understanding, once the first time change occurs, we will execute the TimeChanged block. While we are executing, we will keep getting more and more notifications and processing them as fast as we can, creating a long queue of events still to be processed.

The problem is that after we process the first time change and move on to the next time change, the "real time" is already far ahead, and whatever we are calculating we are calculating for something that happened in the past.

What we would like to do is to ignore all new events until we finish our original processing, and only then start listening again for the event.

Setting up multiple threads is not an option as it doesn't address the issue, and we do not want to process every time change, only those when our resources have been freed up.

Obviously I've used the time change and the above code as a demonstrative example, but it demonstrates concisely and adequately (IMHO) what we are trying to accomplish here.

I would imagine to use some kind of buffer, but my knowledge here is very very limited. Thanks

Thanks for all the answers so far. Will start on implementing it. Will try to document successes / failures.


Solution

  • Well, first the event in question is not being called asynchronously. So Unless you're setting the time on constantly changing threads, the call to set the time won't come back and you won't set it again until all of the events have handled it. If you want to prevent this problem, you need to move event handling to a different thread.

    Ultimately the complexity of the situation and exactly how real-time you want to be can dictate the ultimate answer to this. But, assuming you want something that's fairly robust for a relatively few number of threads (let's say a dozen), here's roughly how I'd go about doing this.

    private var _Callbacks = new List<PropertyChangedEventHandler>();
    
    public event PropertyChangedEventHandler PropertyChanged
    {
        add
        {
            lock(_Callbacks)
                _Callbacks.Add(value);
    
            Thread Worker = new Thread(PollTime);
            Worker.Background = true;
            Worker.Start(value);
        }
        remove
        {
            lock(_Callbacks)
                _Callbacks.Remove(value);
        }
    }
    
    private void PollTime(object callback)
    {
        PropertyChangedEventHandler c = (PropertyChangedEventHandler)callback;
        string LastReported = null;
    
        while(true)
        {
            lock(_Callbacks)
                if (!_Callbacks.Contains(c))
                    return;
    
            if (LastReported != _currentTime)
            {
                LastReported = _currentTime;
                c(this, new PropertyChangedEventArgs(name));
            }
            else
                Thread.Sleep(10);
        }
    }
    
    public string currentTime
    {
        get { return _currentTime; }
        set { _currentTime= value; } 
    }
    

    With that you get thread safety on your events (in case someone tries to subscribe/unsubscribe from them at an inopportune time), and each subscriber gets it's own thread to handle callbacks. The subscribers won't get all the same events, but they will all be notified when the time changes. Slower ones just won't get as many events because they'll lose some of the intermediate values. This won't notify if the time is reset with no change, but I don't see that as much of a loss. You may see problems if values alternate within a limited set, but with time that's not a problem.

    For more reading on delegates, events and how they work, there's a very long, but very good piece at http://www.sellsbrothers.com/writing/delegates.htm