views:

1053

answers:

4

I am writing an API in C# and I want to provide both synchronous and asynchronous versions of the publicly available methods. For example, if I have the following function:

public int MyFunction(int x, int y)
{
   // do something here
   System.Threading.Thread.Sleep(2000);
   return  x * y;

}

how can I create an asynchronous version of the above method (perhaps BeginMyFunction and EndMyFunction)? Are there different ways to achieve the same result, and what are the benefits of the various approaches?

+6  A: 

The generic approach is to use a delegate:

IAsyncResult BeginMyFunction(AsyncCallback callback)
{
    return BeginMyFunction(callback, null);
}

IAsyncResult BeginMyFunction(AsyncCallback callback, object context)
{
    // Func<int> is just a delegate that matches the method signature,
    // It could be any matching delegate and not necessarily be *generic*
    // This generic solution does not rely on generics ;)
    return new Func<int>(MyFunction).BeginInvoke(callback, context);
}

int EndMyFunction(IAsyncResult result)
{
    return new Func<int>(MyFunction).EndInvoke(result);
}
Mehrdad Afshari
+1... if you _have_ to implement this in your business object.
Michael Meadows
BTW, Note that the `new Func<int>` is just for demonstration purposes and should be replaced with the appropriate delegate instance. You should keep the delegate instance around to call `EndInvoke` on it.
Mehrdad Afshari
+1  A: 

You could create a version of the method that takes a delegate to callback:

delegate void PassIntDelegate (int i);
delegate void PassIntIntCallbackDelegate (int i1, int i2, PassIntDelegate callback);

public int MyFunction (int i1, int i2)
{
    return i1 * i2;
}

public void MyFunctionAsync (int i1, int i2, PassIntDelegate callback)
{
    new PassIntIntDelegate (_MyFunctionAsync).BeginInvoke (i1, i2, callback);
}

private void _MyFunctionAsync (int i1, int i2, PassIntDelegate callback)
{
    callback.Invoke (MyFunction (i1, i2));
}

This version isn't as clean as the one using AsyncCallback, but it's a little more type-safe.

ZaijiaN
+2  A: 

First of all, if you're compute-bound, I wouldn't bother. Leave it up to the client to determine whether they want to call you synchronously on the current thread, or asynchronously via ThreadPool.QueueUserWorkItem.

If however, you have some form of I/O in your routine, then it could be beneficial to provide an asynchronous version. You should ensure that your asynchronous version uses the corresponding asynchronous I/O calls. You will also need to implement IAsyncResult and return this from your BeginMyFunction call. See Joe Duffy's implementation here, and some notes on the subtleties of various BCL implementations here.

Nick Gunn
+4  A: 

Mehrdad Afshari answers your question as best as I could suggest. I would, however, advise against this if at all possible. Unless your business object's sole responsibility is to run things synchronously or asynchronously, you're violating the single responsibility principle by even trying to make it aware of the fact that it could run asynchronously. It's easy enough to do this type of operation in the consuming class using anonymous delegates:

public void Foo(int x, int y)
{
    ThreadPool.QueueUserWorkItem(delegate
        {
            // code to execute before running
            myObject.MyFunction(x, y);
            // code to execute after running
        });
}

If you have no code to run before or after, you can use a lambda to make it more concise

ThreadPool.QueueUserWOrkItem(() => myObject.MyFunction(x, y));
Michael Meadows