There are a few options to serialize handling of the events which may be fired concurrently from multiple threads, or on the same thread via re-enrancy/recursion.
One of the options is to use a task scheduler. In your scenario, the purpose of the task scheduler would be to queue callbacks for later serial execution. A well known conceptual examples of the task schedulers like that are WinForms message loop (starting with Application.Run
, queuing with Control.BeginInvoke
) and WPF Dispatcher loop (starting with Dispatcher.Run
, queuing with Dispatcher.BeginInvoke
or Dispatcher.InvokeAsync
).
A less know but very useful implementation of a TPL task scheduler is StaTaskScheduler
by Stephen Toub. I posted some code based upon it here and here (the latter one is from a closely question, Queuing Actions/Delegates for Asyncronous Execution).
Another option is described here: Task sequencing and re-entracy.
In this case, handlers are composed as tasks, each new handler is started when the previous task has completed.