I have an event which I would like to have processed in a parallel manner. My idea is to make each callback be added to the ThreadPool, effectivley having each method that registered the event handled by the ThreadPool.
My try-out code looks something like the following:
Delegate[] delegates = myEvent.GetInvocationList();
IAsyncResult[] results = new IAsyncResult[ delegates.Count<Delegate>() ];
for ( int i = 0; i < delegates.Count<Delegate>(); i++ )
{
IAsyncResult result = ( ( TestDelegate )delegates[ i ] ).BeginInvoke( "BeginInvoke/EndInvoke", null, null );
results[ i ] = result;
}
for ( int i = 0; i < delegates.Length; i++ )
{
( ( TestDelegate )delegates[ i ] ).EndInvoke( results[ i ] );
}
This is just for playing around since I am curious how to do it. I am sure there is a better way to do it. I don't like having a Func which creates a WaitCallback that holds a lambda. Also, DynamicInvoke is pretty slow compared to calling a delegate directly. I doubt this way of processing the event is any faster than just doing it sequentially.
My question is: How can I process an event in a parallel manner, preferably by using ThreadPool?
Since I usually work with Mono, .NET 4.0 or the Task Parallel Library are both not an option.
Thank you!
EDITS: - Corrected example thanks to Earwickers answer. - Updated try-out code
Are you doing this for performance?
That will only work if it allows you to get multiple pieces of hardware working in parallel, and it will cost you in process-switch overhead.
I'd go for an approach using DynamicMethod (LCG) and a state object which carries the arguments and does keep track of the calls (so that you can wait for them to complete).
Code: Something like this should do (not throughly tested yet though, may therefore throw some nasty exceptions in some situations):
Usage:
Notes:
Exceptions in the event handlers are not handled (but the IL code has a finally to decrement the counter, so that the method sould end correctly) and this could cause trouble. It would be possible to catch and transfer the exceptions in the IL code as well.
Implicit conversions other than inheritance (such as int to double) are not performed and will throw an exception.
The synchronization technique used does not allocate OS wait handles, which is usually good for performance. A description of the Monitor workings can be found on Joseph Albahari's page.
After some performance testing, it seems that this approach scales much better than any approach using the "native" BeginInvoke/EndInvoke calls on delegates (at least in the MS CLR).
If the type of the delegate is known, you can directly call their BeginInvoke ans store the IAsyncResults in an array to wait and end the calls. Note that you should call EndInvoke in order to avoid potential resource leaks. The code relies on the fact that EndInvoke waits until the call is finished, so that no WaitAll is required (and, mind you, WaitAll has several issues so that I'd avoid its use).
Here is a code sample, which is at the same time a simplistic benchmark for the different approaches:
On my very old laptop this takes 95553 ms with BeginInvoke/EndInvoke vs. 9038 ms with my ParallelInvoke approach (MS .NET 3.5). So this approach scales not well compared to the ParallelInvoke solution.
You appear to be doing the asynchronous launching twice in your code snippet.
First you call BeginInvoke on a delegate - this queues a work item so that the thread pool will execute the delegate.
Then inside that delegate, you use QueueUserWorkItem to... queue another work item so the thread pool will execute the real delegate.
This means that when you get back an IAsyncResult (and hence a wait handle) from the outer delegate, it will signal completion when the second work item has been queued, not when it has finished executing.