Freigeben über


A Generic IAsyncResult Implementation

Lately, I've found myself writing implementations of IAsyncResult more than once, and as it turns out, all of those implementations have been so similar that I have ended up creating a generic implementation.

When implementing the Async Pattern, you can often get by with using a delegate as described in the design guidelines, and in this case, you don't need to implement IAsyncResult yourself. However, there will be cases where a custom implementation of IAsyncResult may be necessary. In some cases, the folllowing class may be suitable to your needs.

Update: Oleg Mikhailik has been so kind to point out some flaws of the code I first posted (see the comments), so I've decided to update the post with a new version of the code. Again, whether such an implementation is suitable for you depends on your needs. The main point of this post is give an example implementation of IAsyncResult, since so few are available, so this is not an authoritative, general-purpose implementation.

 internal class AsyncResult<T> : IAsyncResult, IDisposable
{
    private readonly AsyncCallback callback_;
    private bool completed_;
    private bool completedSynchronously_;
    private readonly object asyncState_;
    private readonly ManualResetEvent waitHandle_;
    private T result_;
    private Exception e_;
    private readonly object syncRoot_;
 
    internal AsyncResult(AsyncCallback cb, object state)
        : this(cb, state, false)
    {
    }
 
    internal AsyncResult(AsyncCallback cb, object state,
        bool completed)
    {
        this.callback_ = cb;
        this.asyncState_ = state;
        this.completed_ = completed;
        this.completedSynchronously_ = completed;
 
        this.waitHandle_ = new ManualResetEvent(false);
        this.syncRoot_ = new object();
    }
 
    #region IAsyncResult Members
 
    public object AsyncState
    {
        get { return this.asyncState_; }
    }
 
    public WaitHandle AsyncWaitHandle
    {
        get { return this.waitHandle_; }
    }
 
    public bool CompletedSynchronously
    {
        get 
        {
            lock (this.syncRoot_)
            {
                return this.completedSynchronously_;
            }
        }
    }
 
    public bool IsCompleted
    {
        get 
        {
            lock (this.syncRoot_)
            {
                return this.completed_;
            }
        }
    }
 
    #endregion
 
    #region IDisposable Members
 
    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }
 
    #endregion
 
    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            lock (this.syncRoot_)
            {
                if (this.waitHandle_ != null)
                {
                    ((IDisposable)this.waitHandle_).Dispose();
                }
            }
        }
    }
 
    internal Exception Exception
    {
        get
        {
            lock (this.syncRoot_)
            {
                return this.e_;
            }
        }
    }
 
    internal T Result
    {
        get 
        {
            lock (this.syncRoot_)
            {
                return this.result_;
            }
        }
    }
 
    internal void Complete(T result,
        bool completedSynchronously)
    {
        lock (this.syncRoot_)
        {
            this.completed_ = true;
            this.completedSynchronously_ =
                completedSynchronously;
            this.result_ = result;
        }
 
        this.SignalCompletion();
    }
 
    internal void HandleException(Exception e,
        bool completedSynchronously)
    {
        lock (this.syncRoot_)
        {
            this.completed_ = true;
            this.completedSynchronously_ =
                completedSynchronously;
            this.e_ = e;
        }
 
        this.SignalCompletion();
    }
 
    private void SignalCompletion()
    {
        this.waitHandle_.Set();
 
        ThreadPool.QueueUserWorkItem(new WaitCallback(this.InvokeCallback));
    }
 
    private void InvokeCallback(object state)
    {
        if (this.callback_ != null)
        {
            this.callback_(this);
        }
    }
}

As reproduced here, you may have noticed that the class is internal. This is because I usually just copy in this code and use it internally, but if you want to include it in a reusable library, you will obviously need to change the class and relevant members (the constructors, Result and Complete) to public.

When implementing the Async Pattern, you can create and return an instance of AsyncResult<T> and return it from your BeginX method. Then, when your asynchronous operation completes, call the Complete method, which will store the result, release the WaitHandle and invoke any callback supplied by the client. To implement the EndX method, cast the incoming IAsyncResult to AsyncResult<T>, call WaitOne on its AsyncWaitHandle and return its Result property.

This is not the only way to implement IAsyncResult, but it has worked well for me in several cases.

Comments

  • Anonymous
    February 09, 2007
    The comment has been removed

  • Anonymous
    February 09, 2007
    My mail is mi98hai12lik@gmail.com (remove digits).

  • Anonymous
    February 09, 2007
    Hi Mihailik Thank you for your comment. I think it only demonstrates that my way isn't the only way to do it :) As it is right now, the class is internal, so it assumes that you use it together with at least one other, public class. If you were to change it to an entirely public class, I agree that more robustness may be required. Your point about WaitHandle resource usage is valid: It just occurred to me that WaitHandle implements IDisposable, so that should have clued me in on the fact that I should be more conservative about its usage. Incidentally, since AsyncResult<T> has an IDisposable member variable, it should itself implement IDisposable. At least, this issue should be easy to resolve by implementing the AsyncWaitHandle with lazy initialization, and updating the Complete method accordingly. I think you are right about the need for thread-safety around at least some of the member variables. I'll see what I can do about that :) It may be that this implementation of IAsyncResult is not useful in all cases, but then I never claimed that. On the other hand, it has served me well on several occasions. My main motivation for posting this (and thus, sticking my neck out) was that it's actually pretty difficult to find an example implementation of IAsyncResult.

  • Anonymous
    February 18, 2007
    HI.. Im quite interested in seeing how this IAsyncResult would be used in a project.. Im currently implementing my own BeginXXX EndXXX methods and inside i use delgate.beginInvoke with call backs etc.. can this custom IAsyncResult be used with the Delegate.Invoke model? or is it used differently.. Any same projects would be greatly appreciated.  I'm guessing this would be used with a ThreapStart or something along those lines?

  • Anonymous
    February 18, 2007
    Hi Greg Thank you for writing. As I wrote in the introduction to my post, you don't need to write a custom implementation of IAsyncResult if you use the delegate approach. In this case, delegate.BeginInvoke returns an IAsyncResult implementation that you should be able to reuse in your own implementation. HTH

  • Anonymous
    February 22, 2007
    Hi Again. I've now completed my delegate approach, and as you mentioned a custom iasyncresult is not possible.  Do you have an example using this Generic Iasycn for people that take the Threading approach.. Im going to need to develop some async web services/ handlers and from what i hav found a custom iasyncresult combined with threads is the best way?  im just looking for a tidy implementation example :) Cheers

  • Anonymous
    February 23, 2007
    Hi mrhaboobi Thank you for writing. I'm not sure I understand your need, but maybe this article can help answer some of your questions: http://msdn2.microsoft.com/en-us/library/83bkx91t.aspx If this doesn't help, you are welcome to write again and explain what you are trying to do.

  • Anonymous
    March 08, 2007
    You probably already know but Jeffrey Richter has published his generic implementation of IAsyncResult in his Concurrent Affairs column in the March issue of MSDN Magazine: http://msdn.microsoft.com/msdnmag/issues/07/03/ConcurrentAffairs/ Best regards, Jakob.

  • Anonymous
    March 08, 2007
    Hi Jakob Yes, someone else was so nice to point that out to me, but in my defense, that article wasn't published when I wrote my post. I haven't yet had a chance to read the article, but I'm sure Jeffrey Richter's implementation is much better and more correct than mine :)

  • Anonymous
    March 10, 2007
    Ho trovato questo post sui blogs di MSDN. Fornisce un modello generico di IAsyncResult che in effetti

  • Anonymous
    June 04, 2007
    I read Jeffrey Richter's implementation, but I don't think it is as useful as yours. Nice job, thx a lot.

  • Anonymous
    June 04, 2007
    Hi bonbon Thank you for your praise, but after having read Jeffrey Richter's article, I suspect that his implementation is more lightweight and robust than mine :)

  • Anonymous
    June 22, 2008
    dude the first thing you should explain is what this code is for. I was googling for something and your code made no sense to me.

  • Anonymous
    July 29, 2008
    Nice looking implementation.. Just why isn't this class included in .NET as a public built-in abstract base class for everyone to use? There must be some base class for implementing IAsyncResult classes; it wouldn't make any sense if every async method in .NET would have their own..

  • Anonymous
    July 31, 2008
    Hi reisi There are no base classes in the BCL for implementing IAsyncResult, but an implementation like Jeffrey Richter's (see comments above) would be a very good candidate. You can suggest such a class on https://connect.microsoft.com/VisualStudio.

  • Anonymous
    June 30, 2009
    I think you should check the return value of ThreadPool.QueueUserWorkItem and throw an exception if it returns false.

  • Anonymous
    April 07, 2010
    So when exactly do you intend for your IAsyncResult object to be disposed?

  • Anonymous
    October 18, 2010
    Nice helpful piece! Just to nitpick, you should add a private member variable bool "disposed". You should check that it is not already disposed before disposing for safety. if(disposed)   return; // dispose code here disposed = true;

  • Anonymous
    June 16, 2011
    This is incorrect if someone calls Complete(x, true). You'll report CompletedSynchronously as true but you're actually calling the callback on a Threadpool thread, which means it will have completed asynchronously for people registered to the callback.