2

I have an event handler for an event that can occur in rapid succession. In some cases, the handler will still be executing when the event fires again (within milliseconds).

Is there anyway to make these handlers...serial? So, Event Invocation #2 cannot start until Event Invocation #1 has completed?

If this is an awful idea, why? This is a batch process that runs unattended in the background, so I'm not terribly concerned about performance issues that might occur from one invocation blocking another.

Deane
  • 8,269
  • 12
  • 58
  • 108
  • As a remark you would need to have breathing room, otherwise you could eventually run out of resources if you are going to stack them. – Silvermind Feb 21 '14 at 22:25
  • 2
    Are the event handlers running on multiple threads? If not, are the event handlers doing some sort of DoEvents call that creates re-entrancy? If not then *how is this possible*? Event handlers are triggered by pumping a message queue; if the thread is busy running the event handler then by definition it is not busy pumping the queue! – Eric Lippert Feb 21 '14 at 23:48

3 Answers3

3

Use the mult-thread controle lock. This prevents that your code will be run more the one time in same moment.

EDITED
EXAMPLE:

public class Foo
{
    private static object _lockKey = new object();

    private void YourEventHandlerMethod(object sender, EventArgs e)
    {   
        lock (_lockKey)
        {
            // the code you put inside this block will be execute only
            // once at time.

            // If a second call has the intention to use this block before
            // the conclusion of the first call, the second call (the thread)
            // will be put in hold.
        }
    }
}
Jonny Piazzi
  • 3,684
  • 4
  • 34
  • 81
  • But what do I lock? The entire object? Can I "lock" a method (the event handler itself)? – Deane Feb 21 '14 at 22:23
  • Ohhhh. So, you can put any object in there, because the object is really just a flag that the block of code is in use, right? – Deane Feb 21 '14 at 22:37
  • If the handlers are running on the thread pool, be wary of deadlocks when you lock up all the threads in the pool – Dan Bryant Feb 21 '14 at 22:40
  • Yes. But the lock will only work of the realy same object, that was I write _lockKey as static. – Jonny Piazzi Feb 21 '14 at 23:15
  • This is not working for me. I have a similiar issue on Winforms with a nonmodal-dialog. I put the entire Click-event into a lock but I'm still able to create multiple instances of that dialog. Why so? – isHuman Aug 17 '16 at 15:16
  • This not prevent that multiples instances of a class to be created. Lock only prevents that the snippet of code inside to be executed "once per time". I.E. can be executed multiple times, but never in the same moment. (As the most of computers today have multiple processor cores, a few chunk of code is always been executed at the same time of others. Lock controls this paralel, nothing more.) (p.s. it's possible, by tread control paralel, even if there is no multiple cores). – Jonny Piazzi Aug 17 '16 at 21:58
  • 1
    Note that your event handler can still be entered multiple times recursively! This happens if your event code itself triggeres the event on the same thread again because it already *got* the lock and can re-enter it without re-locking. It sounds far-fetched but is very common in UI events, for instance when changing `Text` inside a `TextBox.TextChanged` event. – djk Dec 22 '19 at 10:43
0

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.

Community
  • 1
  • 1
noseratio
  • 59,932
  • 34
  • 208
  • 486
0

Eight years down the road, I've discovered the joy of the blocking collection.

Instead of having the event handler do the work, I could have just put some data in a blocking collection and had a separate thread to work that.

private readonly BlockingCollection<Whatever> queue = new BlockingCollection<Whatever>();

Task.Run(() =>
{
  while (!queue.IsCompleted) {

   // Do whatever

  }
}

// Then just add a bunch of stuff to it
queue.Add(new Whatever());

// It will just keep working the queue in a separate thread, going back for a new Whatever whenever it's done

// You can even start multiple threads to work the queue. They won't interfere with each other
Deane
  • 8,269
  • 12
  • 58
  • 108