The request framework should not require that .NET multithreading is used for asynchronous requests. Otherwise, it would prvent overlapped operations, 3rd party APIs (eg. used via P/Invoke) from being able to use the request framework and possibly even spawn duplicate implementations. Design using interfaces: interface IWaitable { /// Fired when the background process has finished /// /// If the process is already finished when a client registers to this event, /// the registered callback will be invoked synchronously right when the /// registration takes place. /// event EventHandler Finished; /// Waits until the background process finishes void Wait(); /// Waits until the background process finishes or a timeout occurs /// /// Number of milliseconds after which to stop waiting and return immediately /// /// /// True if the background process completed, false if the timeout was reached /// bool Wait(int timeoutMilliseconds); /// Whether the background process has finished bool Finished { get; } // ? /// Wait handle that can be used to wait for multiple waitables /// /// Only use the WaitHandle to wait if you're running in a different thread than /// the request, or you may deadlock. Libraries based on single-threaded /// multitasking may employ concepts such as window message processing to achieve /// parallelism which could be stalled by improper waiting using the WaitHandle /// whereas the Wait() method typically has a suitable implementation compatible /// with the request's requirements. /// WaitHandle WaitHandle { get; } } interface IThreadedWaitable : IWaitable { WaitHandle WaitHandle { get; } } interface IRequest : IWaitable { /// /// Waits for the background process to complete and re-throws the exception to /// the caller when an error has occured /// void Join(); } interface IRequest : IRequest { /// /// Waits for the background process to complete and re-throws the exception to /// the caller when an error has occured /// /// The result of the background processing new ResultType Join(); } interface IThreadedRequest : IRequest, IThreadedWaitable { } interface IThreadedRequest : IRequest, IThreadedRequest { } Impossible implementation: class Request : IRequest { event EventHandler Finished; void Wait(); bool Wait(int timeoutMilliseconds); bool Finished { get; } void Join(); protected virtual void ReraiseExceptions() { } } class Request : Request, IRequest { new ResultType Join(); protected abstract ResultType GatherResults(); } Do not provide: (see conflict in second version) class ThreadedRequest : Request, IThreadedRequest { WaitHandle WaitHandle { get; } } class ThreadedRequest : ThreadedRequest, Request { } // However, not providing these, the user would have to rewrite // the complex threading routines everywhere he uses then. Bad. Inelegant implementation: class Void {} class Request : IRequest { new ResultType Join(); protected abstract ResultType GatherResults(); } class ThreadedRequest : Request { } // However, not providing these, the user would have to rewrite // the complex threading routines everywhere he uses then. Bad. Maybe keeping threaded and non-threaded requests apart is a good thing? IWaitable (without Finished event) Waitable (Finished event) Request Request IWaitable (without Finished event) ThreadedWaitable (AsyncFinished event) ThreadedRequest ThreadedRequest Or just dump the WaitHandle schmonder Waitable (with virtual protected SyncRoot { get { return this; } }) Request Request LazyWaitHandle WaitHandle Get(Waitable waitable) Or use policy classes (waithandle trouble) Waitable Request Request RequestImpl RequestImpl LazyWaitHandle WaitHandle Get(Waitable waitable) WaitHandle Get(Waitable waitable, object syncRoot) ThreadPolicy { virtual void lock() {} virtual void unlock() {} }