dotNET
dotNET

Reputation: 35400

Synchronization of BeginInvoke calls

So I'm calling Dispatcher.BeginInvoke() to perform some UI actions in a Timer.Elapsed event. The timer is ticking fast, and multiple new instances of BeginInvoke() may stack up before a previous call is fully processed. After processing of current call is finished, I'm always interested in picking the latest instance of BeginInvoke() only, i.e. any previous unprocessed instances on the message queue should be discarded.

What is the correct way of emptying the Dispatcher's BeginInvoke queue to achieve this?

To demonstrate an example, consider that I'm reading value from a sensor in Timer.Elapsed event several times a second and then updating a complex UI to show the read values. This UI update action takes some time and during this time, one or more new instances of the read values stack up on the dispatcher queue to get rendered. Obviously, when I have got more recent values from the sensor, I'd want to discard all instances in the waiting line and just keep the current one, to be sent for rendering once the processor is free.

Upvotes: 0

Views: 303

Answers (1)

BionicCode
BionicCode

Reputation: 28988

There is no chance to dequeue callbacks since you are not managing the UI thread. But you could use a CancellationTokenSource:

  1. Pass the CancellationTokenSource (CancellationTokenSource.Token) to the dispatcher and your callback.

  2. Listen for cancellation by repeatedly invoking CancellationToken.ThrowIfCancellationRequested() inside your callback and catch the OperationCanceledException exception that will be thrown once the CancellationTokenSource.Cancel() was called

  3. Use a catch block to catch OperationCanceledException and do the clean up in order to reverse state to prior of executing the callback

  4. Before invoking the dispatcher with a new action you cancel all previous callbacks by invoking CancellationTokenSource.Cancel(). This will trigger the ThrowIfCancellationRequested() to actually throw an OperationCanceledException inside the callback.

  5. Invoke dispatcher with new callback and new CancellationToken from a fresh CancellationTokenSource instance and dispose all cancelled CancellationTokenSource instances.

This way you can cancel the dispatcher action e.g. in case it's long running or prevent it to be executed in case the action is still pending. Otherwise you have to enqueue the next dispatcher action and override the changes of the previous action.

Dispatcher.InvokeAsync(...) is equal to Dispatchher.BeginInvoke(...) but in addition it allows you to pass a cancellation token to the dispatcher.

Upvotes: 2

Related Questions