Providing Synchronous and Asynchronous Versions of

2019-03-30 17:39发布

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?

4条回答
Viruses.
2楼-- · 2019-03-30 17:44

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);
}
查看更多
我命由我不由天
3楼-- · 2019-03-30 17:45

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.

查看更多
唯我独甜
4楼-- · 2019-03-30 18:03

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));

EDIT

In response to @kshahar's comment below, externalizing asynchronicity is still a good idea. This is a common problem that has been solved using callbacks for decades. Lambdas simply shorten the route, and .Net 4.0 makes it even simpler.

public void Foo(int x, int y)
{
    int result = 0; // creates the result to be used later
    var task = Task.Factory.StartNew(() => // creates a new asynchronous task
    {
        // code to execute before running
        result = myObject.MyFunction(x, y);
        // code to execute after running
    });
    // other code
    task.Wait(); // waits for the task to complete before continuing
    // do something with the result.
}

.Net 5 makes it even easier than that, but I'm not familiar enough with it to make a statement beyond that at this point.

查看更多
可以哭但决不认输i
5楼-- · 2019-03-30 18:06

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.

查看更多
登录 后发表回答