我正在用C#编写API,我想提供公开可用方法的同步和异步版本。 例如,如果我有以下功能:
public int MyFunction(int x, int y)
{
// do something here
System.Threading.Thread.Sleep(2000);
return x * y;
}
如何创建上述方法的异步版本(可能是BeginMyFunction和EndMyFunction)?是否有不同的方法来实现相同的结果,各种方法的好处是什么?
答案 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那样干净,但它更安全。