在C#中提供方法的同步和异步版本

时间:2009-02-27 21:00:14

标签: c# .net asynchronous

我正在用C#编写API,我想提供公开可用方法的同步和异步版本。 例如,如果我有以下功能:

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

}

如何创建上述方法的异步版本(可能是BeginMyFunction和EndMyFunction)?是否有不同的方法来实现相同的结果,各种方法的好处是什么?

4 个答案:

答案 0 :(得分:7)

通用方法是使用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);
}

答案 1 :(得分:4)

Mehrdad Afshari尽我所能地回答你的问题。但是,如果可能的话,我会建议不要这样做。除非您的业务对象的唯一责任是同步或异步运行,否则即使尝试让它意识到它可以异步运行,也违反了single responsibility principle。使用匿名委托在消费类中进行这种类型的操作很容易:

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

如果您之前或之后没有代码可以运行,您可以使用lambda使其更简洁

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

修改

回应@kshahar在下面的评论,外部化异步性仍然是一个好主意。这是几十年来使用回调解决的常见问题。 Lambdas简单地缩短了路线,而.Net 4.0使它更简单。

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让它变得更加容易,但我现在还不够熟悉,不能在此之外发表声明。

答案 2 :(得分:2)

首先,如果你受到计算限制,我不会打扰。将其留给客户端以确定他们是想在当前线程上同步调用您,还是通过ThreadPool.QueueUserWorkItem异步调用。

但是,如果您的例程中有某种形式的I / O,那么提供异步版本可能会有所帮助。您应确保异步版本使用相应的异步I / O调用。您还需要implement IAsyncResult并从BeginMyFunction调用中返回此内容。参见Joe Duffy的实现here,以及关于各种BCL实现的微妙之处的一些注释here

答案 3 :(得分:1)

您可以创建一个方法版本,将委托转发给回调:

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

这个版本不像使用AsyncCallback那样干净,但它更安全。