Run event handlers in another thread (without blocking threads)

I have a Communicator class that runs in a background thread receiving data on a TCP port.

The communicator has an OnDataReceived event of the type EventHandler<DataReceivedEventArgs> .

There is another Consumer class that contains a method that subscribes to the Communicator.OnDataReceived event.

 comm.OnDataReceived += consumer.PresentData; 

The Consumer class is created inside the form constructor, and then one of its methods is called in another thread. This method is an infinite loop, so it stays in this method at runtime.

What I would like to do is this Communicator.OnDataReceived event to call the consumer.PresentData method in the user stream.

Is it even almost possible? And if so, what mechanisms (synchronization classes) should I use?

+4
source share
4 answers

Add this somewhere in your code: (Usually I put this in a static helper class called ISynchronizedInvoke, so I can call ISynchronizedInvoke.Invoke (...));

 public static void Invoke(ISynchronizeInvoke sync, Action action) { if (!sync.InvokeRequired) { action(); } else { object[] args = new object[] { }; sync.Invoke(action, args); } } 

Then inside OnDataReceived you can do:

 Invoke(consumer, () => consumer.PresentData()); 

This calls "consumer.PresentData" on the "consumer".

As for your design problem (communicator for consumer links), you can enter a method inside the communicator, for example:

 class Communicator { private ISynchronizeInvoke sync; private Action syncAction; public void SetSync(ISynchronizeInvoke sync, Action action) { this.sync = sync; this.syncAction = action; } protected virtual void OnDataReceived(...) { if (!sync.InvokeRequired) { syncAction(); } else { object[] args = new object[] { }; sync.Invoke(action, args); } } } 

This will give you the opportunity to go to ISynchronizedInvoke from your consumer class. This way you create ISynchronizedInvoke in the assembly for consumers.

 class Consumer { public void Foo() { communicator.SetSync(this, () => this.PresentData()); } } 

So, basically, you create everything you need for a call and simply transfer it to your communicator. This solves your need to have an instance or reference to the consumer in the communicator.

Also note that I have not tested this, I do all of this theoretically, but it should work well.

+4
source

Try using the BackgroundWorker class.

+1
source

It should be possible. You can create a queue for execution or look at the Dispatcher object, it is useful (and sometimes required as the only way) to insert some methods into the user interface thread, this helps.

+1
source

You can get a method to execute on a thread only if the target thread is designed to accept a marshaling operation that transfers the execution of the method from the initiating thread to the target thread.

One way to get this to work is to implement the Consumer class Consumer . Then let your Communicator class accept an instance of ISynchronizeInvoke , which it can use to perform the marshaling operation. As an example, consider the System.Timers.Timer class. System.Timers.Timer has a SynchronizingObject property, which it can use to marshal the Elapsed event on the thread that hosts the synchronization object by calling ISynchronizeInvoke.Invoke or ISynchronizeInvoke.BeginInvoke .

The tricky part is how you implement ISynchronizeInvoke in the Consumer class. The workflow started by this class will need to implement the producer-consumer pattern in order to be able to handle delegates. The BlockingCollection class would make this relatively easy, but there is still a pretty learning curve. If you need more help, take a picture and send a message with a more focused question.

+1
source

All Articles