I am generally wary of implementing interfaces partially. However, IAsyncResult
is a bit of a special case, given that it supports several quite different usage patterns. How often do you use/see used the AsyncState
/AsyncCallback
pattern, as opposed to just calling EndInvoke
, using AsyncWaitHandle
, or polling IsCompleted
(yuck)?
Related question: Detecting that a ThreadPool WorkItem has completed/waiting for completion.
Consider this class (very approximate, locking needed):
public class Concurrent<T> {
private ManualResetEvent _resetEvent;
private T _result;
public Concurrent(Func<T> f) {
ThreadPool.QueueUserWorkItem(_ => {
_result = f();
IsCompleted = true;
if (_resetEvent != null)
_resetEvent.Set();
});
}
public WaitHandle WaitHandle {
get {
if (_resetEvent == null)
_resetEvent = new ManualResetEvent(IsCompleted);
return _resetEvent;
}
public bool IsCompleted {get; private set;}
...
It has WaitHandle
(lazily created, just as described in IAsyncResult
documentation) and IsCompleted
, but I don't see a sensible implementation for AsyncState
({return null;}
?). So does it make sense for it to implement IAsyncResult
? Note that Task
in the Parallel Extensions library does implement IAsyncResult
, but only IsCompleted
is implemented implicitly.
- In my experience, just calling EndInvoke without either waiting or being called back first is rarely useful
- Just providing callbacks is sometimes not enough, as your clients might want to wait for multiple operations at once (WaitAny, WaitAll)
- I've never polled IsCompleted, yuck indeed! So, you could save the implementation of IsCompleted, but it's so simple that it doesn't seem to be worth to potentially astonish your clients.
So, a reasonable implementation for an asynchronously callable method should really provide a fully implemented IAsyncResult.
BTW, you often don't need to implement IAsyncResult yourself, just return what is returned by Delegate.BeginInvoke. See the implementation of System.IO.Stream.BeginRead for an example.
It seems like you have a couple of questions. Let's handle them individually
Creating WaitHandle lazily
Yes this is the most correct approach. You should do this in a thread safe manner but lazy is the way.
The trick though is disposing of the WaitHandle. WaitHandle is a base of IDisposable and must be disposed of in a timely fashion. The documentation for IAsycResult does not cover this case. The best way to do this is in EndInvoke. The documentation for BeginInvoke explicitly states that for every BeginInvoke, there must be a corresponding EndInvoke (or BeginRead/EndRead). This is the best place in which to dispose of the WaitHandle.
How should AsyncState be implemented?
If you look at the standard BCL API's which return an IAsyncResult, most of them take a state parameter. This is typically the value that is returned from AsyncState (see the Socket API's for an example). It is a good practice to include a state variable typed as object for any API BeginInvoke style API which returns an IAsyncResult. Not necessary but good practice.
In the abscence of a state variable, returning null is acceptable.
IsCompleted API
This will be highly dependent on the implementation which creates the IAsyncResult. But yes, you should implement it.