业务逻辑封装在同步服务调用之后,例如:
interface IFooService
{
Foo GetFooById(int id);
int SaveFoo(Foo foo);
}
以异步方式扩展/使用这些服务调用的最佳方式是什么?
目前我已经创建了一个简单的 AsyncUtils 类:
public static class AsyncUtils
{
public static void Execute<T>(Func<T> asyncFunc)
{
Execute(asyncFunc, null, null);
}
public static void Execute<T>(Func<T> asyncFunc, Action<T> successCallback)
{
Execute(asyncFunc, successCallback, null);
}
public static void Execute<T>(Func<T> asyncFunc, Action<T> successCallback, Action<Exception> failureCallback)
{
ThreadPool.UnsafeQueueUserWorkItem(state => ExecuteAndHandleError(asyncFunc, successCallback, failureCallback), null);
}
private static void ExecuteAndHandleError<T>(Func<T> asyncFunc, Action<T> successCallback, Action<Exception> failureCallback)
{
try
{
T result = asyncFunc();
if (successCallback != null)
{
successCallback(result);
}
}
catch (Exception e)
{
if (failureCallback != null)
{
failureCallback(e);
}
}
}
}
这让我可以异步调用任何东西:
AsyncUtils(
() => _fooService.SaveFoo(foo),
id => HandleFooSavedSuccessfully(id),
ex => HandleFooSaveError(ex));
虽然这在简单的用例中有效,但如果其他进程需要协调结果,它很快就会变得棘手,例如,如果我需要在当前线程继续之前异步保存三个对象,那么我想要一种等待的方法-在/加入工作线程。
目前我想到的选项包括:
- 让 AsyncUtils 返回一个 WaitHandle
- 让 AsyncUtils 使用 AsyncMethodCaller 并返回 IAsyncResult
- 重写 API 以包含开始、结束异步调用
例如类似于:
interface IFooService
{
Foo GetFooById(int id);
IAsyncResult BeginGetFooById(int id);
Foo EndGetFooById(IAsyncResult result);
int SaveFoo(Foo foo);
IAsyncResult BeginSaveFoo(Foo foo);
int EndSaveFoo(IAsyncResult result);
}
我应该考虑其他方法吗?每种方法的优点和潜在缺陷是什么?
理想情况下,我希望保持服务层简单/同步,并提供一些易于使用的实用方法来异步调用它们。我有兴趣了解适用于 C# 3.5 和 C# 4 的解决方案和想法(我们尚未升级,但会在不久的将来升级)。
期待您的想法。
最佳答案
如果您要求只保留 .NET 2.0,而不是在 3.5 或 4.0 上工作,这可能是最佳选择。
我对您当前的实现有三点意见。
您使用 ThreadPool.UnsafeQueueUserWorkItem 是否有特定原因? ?除非有特定原因,否则我建议使用 ThreadPool.QueueUserWorkItem相反,特别是如果您在一个大型开发团队中。 Unsafe 版本可能会在您丢失调用堆栈时出现安全漏洞,从而导致无法严格控制权限。
您当前的异常处理设计,使用
failureCallback
, 将吞下所有异常,并且不提供任何反馈,除非定义了回调。如果您不打算正确处理它,传播异常并让它冒泡可能会更好。或者,您可以以某种方式将它推回调用线程,尽管这需要使用更像IAsyncResult
的东西。 .您目前无法判断异步调用是否已完成。这将是使用 IAsyncResult 的另一个优势。在您的设计中(尽管它确实增加了一些实现的复杂性)。
但是,一旦升级到 .NET 4,我建议只将它放在 Task 中。或 Task<T>
,因为它旨在非常干净地处理这个问题。而不是:
AsyncUtils(
() => _fooService.SaveFoo(foo),
id => HandleFooSavedSuccessfully(id),
ex => HandleFooSaveError(ex));
您可以使用内置工具,只需编写:
var task = Task.Factory.StartNew(
() => return _fooService.SaveFoo(foo) );
task.ContinueWith(
t => HandleFooSavedSuccessfully(t.Result),
TaskContinuationOptions.NotOnFaulted);
task.ContinueWith(
t => try { t.Wait(); } catch( Exception e) { HandleFooSaveError(e); },
TaskContinuationOptions.OnlyOnFaulted );
诚然,最后一行有点奇怪,但这主要是因为我试图保留您现有的 API。如果你稍微修改一下,你可以简化它......
关于c# - C#异步调用同步服务调用的策略,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3813849/