EventsHelper using Fire and Forget

By Fons Sonnemans, posted on
2769 Views

I have used the EventsHelper class to fire events asynchronical. I came across this class in the TechEd 2003 presentation C# Best Practices from Eric Gunnerson and Juval Löwy. Today I noticed a bug. The FireAsync() method uses the 'Fire and Forget' pattern incorrectly.

The 'Fire and Forget' pattern is used when the return value and returned parameters of the call are not required, and when there is no need to synchronize with the asynchronously executing method. In this case, the caller simply needs to call BeginInvoke, passing any normal and ref parameters, and null for the callback and asyncState.

This has been a commonly used pattern. However, there has been a documentation change in version 1.1 of the .NET Framework that has big ramifications for this technique of making async calls. The documentation now states that EndInvoke must be called for a corresponding BeginInvoke--otherwise Microsoft say they may now, or in the future, leak resources. It appears that no resources are leaked under version 1.0 of the framework; however, with this type of warning in place, it is recommended that a call to EndInvoke be made even if the return values of an async call are not required.

It is relatively straightforward to create a helper class that handles this for you--one example I found here.

public class AsyncHelper {

   &nbspdelegatevoid DynamicInvokeShimProc(Delegate d,object[]args);

   &nbspstatic DynamicInvokeShimProc dynamicInvokeShim=new
   &nbsp   &nbspDynamicInvokeShimProc(DynamicInvokeShim);

   &nbspstatic AsyncCallback dynamicInvokeDone=new
   &nbsp   &nbspAsyncCallback(DynamicInvokeDone);

   &nbsppublicstaticvoid FireAndForget(Delegate d,paramsobject[]args){
   &nbsp   &nbspdynamicInvokeShim.BeginInvoke(d,args,dynamicInvokeDone,null);
   &nbsp}

   &nbspstaticvoid DynamicInvokeShim(Delegate d,object[]args){
   &nbsp   &nbspd.DynamicInvoke(args);
   &nbsp}

   &nbspstaticvoid DynamicInvokeDone(IAsyncResult ar){
   &nbsp   &nbspdynamicInvokeShim.EndInvoke(ar);
   &nbsp}
}

My improved EventsHelper class now looks like this:

public class EventsHelper {

   &nbsppublicstaticvoid FireAsync(Delegate del,paramsobject[]args){
   &nbsp   &nbspif(del==null){
   &nbsp   &nbsp   &nbspreturn;
   &nbsp   &nbsp}
   &nbsp   &nbspDelegate[]delegates=del.GetInvocationList();
   &nbsp   &nbspAsyncFire asyncFire;
   &nbsp   &nbspforeach(Delegate sinkindelegates){
   &nbsp   &nbsp   &nbspasyncFire=new AsyncFire(InvokeDelegate);
   &nbsp   &nbsp   &nbspAsyncHelper.FireAndForget(asyncFire,sink,args);
   &nbsp   &nbsp}
   &nbsp}

   &nbspdelegatevoid AsyncFire(Delegate del,object[]args);

   &nbsp[OneWay]
   &nbspstaticvoid InvokeDelegate(Delegate del,object[]args){
   &nbsp   &nbspdel.DynamicInvoke(args);
   &nbsp}

   &nbsppublicstaticvoid Fire(Delegate del,paramsobject[]args){
   &nbsp   &nbspif(del==null){
   &nbsp   &nbsp   &nbspreturn;
   &nbsp   &nbsp}
   &nbsp   &nbspDelegate[]delegates=del.GetInvocationList();
   &nbsp   &nbspforeach(Delegate sinkindelegates){
   &nbsp   &nbsp   &nbsptry{
   &nbsp   &nbsp   &nbsp   &nbspsink.DynamicInvoke(args);
   &nbsp   &nbsp   &nbsp}
   &nbsp   &nbsp   &nbspcatch{}
   &nbsp   &nbsp}
   &nbsp}
}

All postings/content on this blog are provided "AS IS" with no warranties, and confer no rights. All entries in this blog are my opinion and don't necessarily reflect the opinion of my employer or sponsors. The content on this site is licensed under a Creative Commons Attribution By license.

Leave a comment

Blog comments

0 responses